From f094fba825f938ae4be79e49e9dfe62c4605af4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 07:23:32 +0000 Subject: [PATCH 1/2] chore: bump ruff from 0.15.22 to 0.16.0 in the pip-development group Bumps the pip-development group with 1 update: [ruff](https://github.com/astral-sh/ruff). Updates `ruff` from 0.15.22 to 0.16.0 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.15.22...0.16.0) --- updated-dependencies: - dependency-name: ruff dependency-version: 0.16.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: pip-development ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e9eab80..6c29b22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dev = [ "pytest-timeout==2.4.0", "pytest-asyncio==1.4.0", "pytest-cov==7.1.0", - "ruff==0.15.22", + "ruff==0.16.0", "bandit==1.9.4", "pyright==1.1.411", "twine==6.2.0", From bde1ae86484b76dd7726301e6f36e04ecb6f98eb Mon Sep 17 00:00:00 2001 From: Golo Roden Date: Sat, 1 Aug 2026 09:35:42 +0200 Subject: [PATCH 2/2] chore: adjust code to ruff 0.16 default rule set Ruff 0.16 expands the default rule selection from 59 to 413 rules, which surfaced 39 findings in this code base. Fix all of them and refresh uv.lock, which had drifted behind pyproject.toml. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01F3ADPwk11GHQF7BBwQHaPz --- eventsourcingdb/__init__.py | 15 +++++- eventsourcingdb/client.py | 29 +++++------- eventsourcingdb/container.py | 22 +++++---- eventsourcingdb/event/event.py | 22 ++++----- eventsourcingdb/http_client/__init__.py | 4 +- eventsourcingdb/http_client/http_client.py | 5 +- eventsourcingdb/http_client/response.py | 5 +- eventsourcingdb/is_valid_server_header.py | 5 +- .../observe_events/observe_events_options.py | 11 ++--- eventsourcingdb/pandas.py | 2 +- tests/conftest.py | 8 ++-- tests/event/test_verify_hash.py | 5 +- tests/event/test_verify_signature.py | 9 ++-- tests/shared/database.py | 26 ++++++----- uv.lock | 46 +++++++++---------- 15 files changed, 114 insertions(+), 100 deletions(-) diff --git a/eventsourcingdb/__init__.py b/eventsourcingdb/__init__.py index 6a6f63d..99ca576 100644 --- a/eventsourcingdb/__init__.py +++ b/eventsourcingdb/__init__.py @@ -1,7 +1,13 @@ from .bound import Bound, BoundType from .client import Client from .container import Container -from .errors import ClientError, CustomError, InternalError, ServerError, ValidationError +from .errors import ( + ClientError, + CustomError, + InternalError, + ServerError, + ValidationError, +) from .event import Event, EventCandidate from .observe_events import ( IfEventIsMissingDuringObserve, @@ -9,7 +15,12 @@ ObserveFromLatestEvent, ) from .read_event_types import EventType -from .read_events import IfEventIsMissingDuringRead, Order, ReadEventsOptions, ReadFromLatestEvent +from .read_events import ( + IfEventIsMissingDuringRead, + Order, + ReadEventsOptions, + ReadFromLatestEvent, +) from .write_events import ( IsEventQlQueryTrue, IsSubjectOnEventId, diff --git a/eventsourcingdb/client.py b/eventsourcingdb/client.py index 578e498..d5d9ce4 100644 --- a/eventsourcingdb/client.py +++ b/eventsourcingdb/client.py @@ -1,28 +1,23 @@ +import json from collections import OrderedDict from collections.abc import AsyncGenerator - -from types import TracebackType -from typing import Any, TypeAlias, TypeVar - from http import HTTPStatus -import json +from types import TracebackType +from typing import Any, Self, TypeAlias, TypeVar +from .errors import CustomError, InternalError, ServerError, ValidationError +from .event import Event, EventCandidate +from .http_client import HttpClient, Response +from .is_event import is_event from .is_heartbeat import is_heartbeat from .is_stream_error import is_stream_error -from .is_event import is_event from .is_valid_server_header import is_valid_server_header -from .parse_raw_message import parse_raw_message -from .read_events import ReadEventsOptions - -from .errors import CustomError, InternalError, ServerError, ValidationError -from .event import Event, EventCandidate from .observe_events import ObserveEventsOptions +from .parse_raw_message import parse_raw_message from .read_event_types import EventType, is_event_type +from .read_events import ReadEventsOptions from .read_subjects import is_subject - from .write_events import Precondition -from .http_client import HttpClient, Response - T = TypeVar('T') @@ -35,7 +30,7 @@ SubjectStream: TypeAlias = AsyncGenerator[str, None] -class Client(): +class Client: def __init__( self, base_url: str, @@ -43,13 +38,13 @@ def __init__( ) -> None: self.__http_client = HttpClient(base_url=base_url, api_token=api_token) - async def __aenter__(self) -> 'Client': + async def __aenter__(self) -> Self: await self.__http_client.__aenter__() return self async def __aexit__( self, - exc_type: BaseException | None = None, + exc_type: type[BaseException] | None = None, exc_val: BaseException | None = None, exc_tb: TracebackType | None = None, ) -> None: diff --git a/eventsourcingdb/container.py b/eventsourcingdb/container.py index 7fded67..8f4e798 100644 --- a/eventsourcingdb/container.py +++ b/eventsourcingdb/container.py @@ -6,12 +6,14 @@ import docker import requests -from docker import DockerClient, errors -from cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ed25519 +from docker import DockerClient, errors from .client import Client +logger = logging.getLogger(__name__) + class Container: def __init__( @@ -33,19 +35,19 @@ def _cleanup_existing_containers(self) -> None: filters={"ancestor": f"{self._image_name}:{self._image_tag}"} ) except errors.APIError as e: - logging.warning("Warning: Error listing existing containers: %s", e) + logger.warning("Warning: Error listing existing containers: %s", e) return for container in containers: try: container.stop() except errors.APIError as e: - logging.warning("Warning: Error stopping container: %s", e) + logger.warning("Warning: Error stopping container: %s", e) try: container.remove() except errors.APIError as e: - logging.warning("Warning: Error removing container: %s", e) + logger.warning("Warning: Error removing container: %s", e) def _create_container(self) -> None: port_bindings = {f"{self._internal_port}/tcp": None} @@ -200,7 +202,7 @@ def _handle_image_pull_error(self, error) -> None: f"Could not pull image and no local image available: {error}" ) from error - logging.warning("Warning: Could not pull image: %s. Using locally cached image.", error) + logger.warning("Warning: Could not pull image: %s. Using locally cached image.", error) def stop(self) -> None: self._stop_and_remove_container() @@ -212,16 +214,16 @@ def _stop_and_remove_container(self) -> None: try: self._container.stop() except errors.NotFound as e: - logging.warning("Warning: Container not found while stopping: %s", e) + logger.warning("Warning: Container not found while stopping: %s", e) except errors.APIError as e: - logging.warning("Warning: API error while stopping container: %s", e) + logger.warning("Warning: API error while stopping container: %s", e) try: self._container.remove() except errors.NotFound as e: - logging.warning("Warning: Container not found while removing: %s", e) + logger.warning("Warning: Container not found while removing: %s", e) except errors.APIError as e: - logging.warning("Warning: API error while removing container: %s", e) + logger.warning("Warning: API error while removing container: %s", e) self._container = None self._mapped_port = None diff --git a/eventsourcingdb/event/event.py b/eventsourcingdb/event/event.py index 6da3823..a5da2c1 100644 --- a/eventsourcingdb/event/event.py +++ b/eventsourcingdb/event/event.py @@ -1,6 +1,6 @@ +import json from dataclasses import dataclass, field from datetime import datetime -import json from hashlib import sha256 from typing import Any, TypeVar @@ -108,16 +108,16 @@ def parse(unknown_object: dict) -> "Event": return event def verify_hash(self) -> None: - metadata = "|".join([ - self.spec_version, - self.event_id, - self.predecessor_hash, - self._time_from_server, - self.source, - self.subject, - self.type, - self.data_content_type, - ]) + metadata = ( + f"{self.spec_version}|" + f"{self.event_id}|" + f"{self.predecessor_hash}|" + f"{self._time_from_server}|" + f"{self.source}|" + f"{self.subject}|" + f"{self.type}|" + f"{self.data_content_type}" + ) metadata_bytes = metadata.encode("utf-8") data_bytes = json.dumps( diff --git a/eventsourcingdb/http_client/__init__.py b/eventsourcingdb/http_client/__init__.py index 06efa7e..878f044 100644 --- a/eventsourcingdb/http_client/__init__.py +++ b/eventsourcingdb/http_client/__init__.py @@ -4,8 +4,8 @@ from .response import Response __all__ = [ - "get_get_headers", - "get_post_headers", "HttpClient", "Response", + "get_get_headers", + "get_post_headers", ] diff --git a/eventsourcingdb/http_client/http_client.py b/eventsourcingdb/http_client/http_client.py index 9dba741..3511a50 100644 --- a/eventsourcingdb/http_client/http_client.py +++ b/eventsourcingdb/http_client/http_client.py @@ -1,4 +1,5 @@ from types import TracebackType +from typing import Self import aiohttp from aiohttp import ClientSession @@ -18,13 +19,13 @@ def __init__( self.__api_token = api_token self.__session: ClientSession | None = None - async def __aenter__(self) -> 'HttpClient': + async def __aenter__(self) -> Self: await self.__initialize() return self async def __aexit__( self, - exc_type: BaseException | None = None, + exc_type: type[BaseException] | None = None, exc_val: BaseException | None = None, exc_tb: TracebackType | None = None, ) -> None: diff --git a/eventsourcingdb/http_client/response.py b/eventsourcingdb/http_client/response.py index 1e64768..c57b075 100644 --- a/eventsourcingdb/http_client/response.py +++ b/eventsourcingdb/http_client/response.py @@ -1,5 +1,6 @@ from collections.abc import Mapping from http import HTTPStatus +from typing import Self import aiohttp from aiohttp import StreamReader @@ -11,14 +12,14 @@ class Response: def __init__(self, response: aiohttp.ClientResponse) -> None: self.__response: aiohttp.ClientResponse = response - async def __aenter__(self) -> "Response": + async def __aenter__(self) -> Self: return self async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: if not self.__response.closed: self.__response.close() - def __enter__(self) -> "Response": + def __enter__(self) -> Self: return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: diff --git a/eventsourcingdb/is_valid_server_header.py b/eventsourcingdb/is_valid_server_header.py index ab21472..e8f723b 100644 --- a/eventsourcingdb/is_valid_server_header.py +++ b/eventsourcingdb/is_valid_server_header.py @@ -7,7 +7,4 @@ def is_valid_server_header(response: Response) -> bool: if not server_header: return False - if not server_header.startswith('EventSourcingDB/'): - return False - - return True + return server_header.startswith('EventSourcingDB/') diff --git a/eventsourcingdb/observe_events/observe_events_options.py b/eventsourcingdb/observe_events/observe_events_options.py index ae5bdc0..f565bc5 100644 --- a/eventsourcingdb/observe_events/observe_events_options.py +++ b/eventsourcingdb/observe_events/observe_events_options.py @@ -18,12 +18,11 @@ def validate(self) -> None: "ObserveEventsOptions are invalid: lower_bound must be a Bound object." ) - if self.from_latest_event is not None: - if self.lower_bound is not None: - raise ValidationError( - "ReadEventsOptions are invalid: " - "lowerBound and fromLatestEvent are mutually exclusive" - ) + if self.from_latest_event is not None and self.lower_bound is not None: + raise ValidationError( + "ReadEventsOptions are invalid: " + "lowerBound and fromLatestEvent are mutually exclusive" + ) def to_json(self) -> dict[str, Any]: result: dict[str, Any] = { diff --git a/eventsourcingdb/pandas.py b/eventsourcingdb/pandas.py index 37a6348..b53a5f9 100644 --- a/eventsourcingdb/pandas.py +++ b/eventsourcingdb/pandas.py @@ -1,4 +1,4 @@ -from typing import AsyncGenerator +from collections.abc import AsyncGenerator import pandas as pd diff --git a/tests/conftest.py b/tests/conftest.py index 22e1080..fe1455e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +from typing import ClassVar + import pytest_asyncio from eventsourcingdb import EventCandidate @@ -20,9 +22,9 @@ class TestData: LOGGED_IN_SUBJECT = "/users/logged-in" REGISTERED_TYPE = "io.thenativeweb.users.registered" LOGGED_IN_TYPE = "io.thenativeweb.users.logged-in" - JANE_DATA = {"name": "jane"} - JOHN_DATA = {"name": "john"} - APFEL_FRED_DATA = {"name": "apfel fred"} + JANE_DATA: ClassVar[dict[str, str]] = {"name": "jane"} + JOHN_DATA: ClassVar[dict[str, str]] = {"name": "john"} + APFEL_FRED_DATA: ClassVar[dict[str, str]] = {"name": "apfel fred"} TRACE_PARENT_1 = "00-10000000000000000000000000000000-1000000000000000-00" TRACE_PARENT_2 = "00-20000000000000000000000000000000-2000000000000000-00" TRACE_PARENT_3 = "00-30000000000000000000000000000000-3000000000000000-00" diff --git a/tests/event/test_verify_hash.py b/tests/event/test_verify_hash.py index 3f1435e..19f0651 100644 --- a/tests/event/test_verify_hash.py +++ b/tests/event/test_verify_hash.py @@ -1,8 +1,9 @@ +from hashlib import sha256 + import pytest from eventsourcingdb import EventCandidate from eventsourcingdb.errors.validation_error import ValidationError -from hashlib import sha256 from ..conftest import TestData from ..shared.database import Database @@ -50,7 +51,7 @@ async def test_fails_if_the_event_hash_is_invalid( written_event = written_events[0] - invalid_hash_data = "invalid data".encode("utf-8") + invalid_hash_data = b"invalid data" invalid_hash = sha256(invalid_hash_data).hexdigest() written_event.hash = invalid_hash diff --git a/tests/event/test_verify_signature.py b/tests/event/test_verify_signature.py index 759438e..2c9e25e 100644 --- a/tests/event/test_verify_signature.py +++ b/tests/event/test_verify_signature.py @@ -1,9 +1,10 @@ -import pytest +from hashlib import sha256 +import pytest from cryptography.hazmat.primitives.asymmetric import ed25519 -from eventsourcingdb import EventCandidate, Container + +from eventsourcingdb import Container, EventCandidate from eventsourcingdb.errors.validation_error import ValidationError -from hashlib import sha256 from ..conftest import TestData @@ -70,7 +71,7 @@ async def test_returns_error_if_hash_verification_fails( written_event = written_events[0] assert written_event.signature is not None - invalid_hash_data = "invalid hash".encode("utf-8") + invalid_hash_data = b"invalid hash" invalid_hash = sha256(invalid_hash_data).hexdigest() written_event.hash = invalid_hash diff --git a/tests/shared/database.py b/tests/shared/database.py index c3b1d46..5a94a15 100644 --- a/tests/shared/database.py +++ b/tests/shared/database.py @@ -1,11 +1,15 @@ +import asyncio import logging import os -import time import uuid +from types import TracebackType +from typing import Self from eventsourcingdb.client import Client from eventsourcingdb.container import Container +logger = logging.getLogger(__name__) + class Database: __create_key = object() @@ -54,9 +58,9 @@ async def create(cls, max_retries=3, retry_delay=2.0) -> 'Database': except OSError as caught_error: error = caught_error retry = True - except Exception as unexpected_error: + except Exception: container.stop() - raise unexpected_error + raise else: retry = False @@ -65,11 +69,11 @@ async def create(cls, max_retries=3, retry_delay=2.0) -> 'Database': container.stop() msg = f'Failed to initialize database container after {max_retries} attempts' raise RuntimeError(f'{msg}: {error}') from error - logging.warning( + logger.warning( 'Container startup attempt %d failed: %s. Retrying in %s seconds...', attempt + 1, error, retry_delay ) - time.sleep(retry_delay) + await asyncio.sleep(retry_delay) container.stop() container = cls._create_container(api_token, "latest") continue @@ -82,9 +86,9 @@ async def create(cls, max_retries=3, retry_delay=2.0) -> 'Database': container, api_token ) - except Exception as client_error: + except Exception: container.stop() - raise client_error + raise return cls(Database.__create_key, with_authorization_client, with_invalid_url_client) @@ -107,14 +111,14 @@ def get_client(self, client_type: str = CLIENT_TYPE_WITH_AUTH) -> Client: raise ValueError(f'Unknown client type: {client_type}') - async def __aenter__(self) -> 'Database': + async def __aenter__(self) -> Self: return self async def __aexit__( self, exc_type: type[BaseException] | None = None, exc_val: BaseException | None = None, - exc_tb: object | None = None + exc_tb: TracebackType | None = None ) -> None: await self.stop() @@ -129,13 +133,13 @@ async def stop(self) -> None: try: await self.__with_authorization_client.__aexit__(None, None, None) except (ConnectionError) as e: - logging.warning("Error closing authorization client: %s", e) + logger.warning("Error closing authorization client: %s", e) if self.__with_invalid_url_client: try: await self.__with_invalid_url_client.__aexit__(None, None, None) except (ConnectionError) as e: - logging.warning("Error closing invalid URL client: %s", e) + logger.warning("Error closing invalid URL client: %s", e) # Then stop the container if (container := getattr(self.__class__, '_Database__container', None)): diff --git a/uv.lock b/uv.lock index 76c73bc..e2023c2 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.11, <=3.13" +requires-python = ">=3.11, <3.15" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", "python_full_version >= '3.14' and sys_platform == 'emscripten'", @@ -532,7 +532,7 @@ dev = [ { name = "pytest-asyncio", specifier = "==1.4.0" }, { name = "pytest-cov", specifier = "==7.1.0" }, { name = "pytest-timeout", specifier = "==2.4.0" }, - { name = "ruff", specifier = "==0.15.21" }, + { name = "ruff", specifier = "==0.16.0" }, { name = "twine", specifier = "==6.2.0" }, ] pandas = [{ name = "pandas", specifier = ">=2.0.0" }] @@ -1311,27 +1311,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.21" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/36/6f65aa9989acdec45d417192d8f4e7921931d8a6cf87ac74bce3eed98a8e/ruff-0.15.21.tar.gz", hash = "sha256:d0cfc841c572283c36548f82664a54ce6565567f1b0d5b4cf2caac693d8b7500", size = 4769401, upload-time = "2026-07-09T20:01:34.005Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/c6/ede15cac6839f3dbce52565c8f5164a8210e669c7bc4decb03e5bdf47d0d/ruff-0.15.21-py3-none-linux_armv6l.whl", hash = "sha256:63ea0e965e5d73c90e95b2434beeafc70820536717f561b32ab6e777cb9bdf5d", size = 10854342, upload-time = "2026-07-09T20:00:53.998Z" }, - { url = "https://files.pythonhosted.org/packages/28/9d/d825b07ee7ea9e2d61df92a860033c94e06e7300d50a1c2653aac27d24fe/ruff-0.15.21-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0f212c5d7d54c01bbfe6dcab02b724a39300f3e34ed7acbe995ccb320a2c58bd", size = 11139539, upload-time = "2026-07-09T20:00:57.809Z" }, - { url = "https://files.pythonhosted.org/packages/f5/de/3b107712e642f063c7a9e0887c427b22cb44097de5aab36c05f2e280670c/ruff-0.15.21-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e6312e41bc96791299614995ea3a977c5857c3b5662b1ecef6755b02b87cb646", size = 10595437, upload-time = "2026-07-09T20:01:00.006Z" }, - { url = "https://files.pythonhosted.org/packages/9a/6f/b4523cc90ba239ede441447a19d0c968846a3012e5a0b0c5b62831a3d5e3/ruff-0.15.21-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01d65b4831c6b2a4ba8ee6faa84049d44d982b7a706e622c4094c509e51673be", size = 10990053, upload-time = "2026-07-09T20:01:02.187Z" }, - { url = "https://files.pythonhosted.org/packages/92/cc/c6a9872a5375f0628875481cf2f66b13d7d865bf3ca2e57f91c7e762d976/ruff-0.15.21-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c5a913a589120ce67933d5d05fd6ddbcc2481c6a054980ee767f7414c72b4fd", size = 10666096, upload-time = "2026-07-09T20:01:04.299Z" }, - { url = "https://files.pythonhosted.org/packages/ab/97/c621f7a17e097f1790fa3af6374138823b330b2d03fc38337945daca212c/ruff-0.15.21-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef04b681d02ad4dc9620f00f83ac5c22f652d0e9a9cfe431d219b16ad5ccc41", size = 11537011, upload-time = "2026-07-09T20:01:06.771Z" }, - { url = "https://files.pythonhosted.org/packages/ea/51/d928727e476e25ccc57c6f449ffd80241a651a973ad949d39cfb2a771d28/ruff-0.15.21-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16d090c0740916594157e75b80d666eab8e78083b39b3b0e1d698f4670a17b86", size = 12347101, upload-time = "2026-07-09T20:01:08.859Z" }, - { url = "https://files.pythonhosted.org/packages/1e/88/8cd62026802b16018ad06931d87997cf795ba2a6239ab659606c87d96bf0/ruff-0.15.21-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a10e74757dd65004d779b73e2f3c5210156d9980b41224d50d2ebcf1db51e67", size = 11572001, upload-time = "2026-07-09T20:01:11.092Z" }, - { url = "https://files.pythonhosted.org/packages/b2/97/f63084cf55444fc110e8cb985ebfcc592af47f597d44453d778cb81bc156/ruff-0.15.21-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bab0905d2f29e0d9fbc3c373ed23db0095edaa3f71f1f4f519ec15134d9e85c8", size = 11549239, upload-time = "2026-07-09T20:01:13.27Z" }, - { url = "https://files.pythonhosted.org/packages/9d/77/f107da4a2874b7715914b03f09ba9c54424de3ff8a1cc5d015d3ee2ce0ac/ruff-0.15.21-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:00eca240af5789fec6fe7df74c088cc1f9644ed83027113468efba7c92b94075", size = 11535340, upload-time = "2026-07-09T20:01:15.206Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e9/601deb322d3303a7bf212b0100ead6f2ee3f6a044d89c30f2f92bf83c731/ruff-0.15.21-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:262ab31557a75141325e32d3357f3597645a7f084e732b6b054dde428ecd9341", size = 10964048, upload-time = "2026-07-09T20:01:17.723Z" }, - { url = "https://files.pythonhosted.org/packages/ea/2e/0f2176d1e99c15192caea19c8c3a0a955246b4cb4de795042eeb616345cd/ruff-0.15.21-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:659c4e7a4212f83306045ec7c5e5a356d16d9a6ef4ae0c7a4d872914fc655d9d", size = 10667055, upload-time = "2026-07-09T20:01:19.73Z" }, - { url = "https://files.pythonhosted.org/packages/48/60/abd74a02e0c4214f12a68becfd30af7165cfdcb0e661ecdc60bbb949c09a/ruff-0.15.21-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9e866eab611a5f959d36df2d10e446973a3610bc42b0c15b31dc27977d59c233", size = 11242043, upload-time = "2026-07-09T20:01:21.947Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c6/583075d8ccabb4b229345edcaf1545eb3d8d6be90f686a479d7e94088bbf/ruff-0.15.21-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e89bc93c0d3803ba870b55c29671bad9dc6d94bb1eb181b056b52eb05b52854f", size = 11648064, upload-time = "2026-07-09T20:01:24.023Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3c/37d0ecb729a7cc2d393ea7dce316fc585680f35d93b8d62139d7d0a3700c/ruff-0.15.21-py3-none-win32.whl", hash = "sha256:01f8d5be84823c172b389e123174f781f9daf86d6c58719d603f941932195cdd", size = 10896555, upload-time = "2026-07-09T20:01:26.941Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b8/e43466b2a6067ce91e669068f6e28d6c719a920f014b070d5c8731725de3/ruff-0.15.21-py3-none-win_amd64.whl", hash = "sha256:d4b8d9a2f0f12b816b50447f6eccb9f4bb01a6b82c86b50fb3b5354b458dc6d3", size = 12038772, upload-time = "2026-07-09T20:01:29.497Z" }, - { url = "https://files.pythonhosted.org/packages/dd/75/e90ab9aeece218a9fc5a5bc3ec97d0ee6bb3c4ff95869463c1de58e29a1c/ruff-0.15.21-py3-none-win_arm64.whl", hash = "sha256:6e83115d4b9377c1cbc13abf0e051f069fab0ef815ea0504a8a008cee24dd0a8", size = 11375265, upload-time = "2026-07-09T20:01:31.772Z" }, +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/94/1e5e4967626faf12fa56999cd6222dff6992ceb086ad7945756baf70c7a7/ruff-0.16.0.tar.gz", hash = "sha256:e460aafd5495ec89efaa6ced2e4a9a581116451e1c88b9d37ef497e0f8e93982", size = 4790557, upload-time = "2026-07-23T19:11:30.981Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/81/1c8818fee7ce1a04cd7d1b3172e0a8f8e4f1dc4feb7fc390e16daa8af323/ruff-0.16.0-py3-none-linux_armv6l.whl", hash = "sha256:e5115729eb08c585e5121978ba5d5b60caeae394ce21b9fb5e6cd33a1c6c9b1e", size = 10754633, upload-time = "2026-07-23T19:10:46.415Z" }, + { url = "https://files.pythonhosted.org/packages/23/df/beaf59c09d68db84304d555f188b276a77132a5d5b0b67a5c762aa143628/ruff-0.16.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c954b1d580bfa035b41654f7858cc7e71d5fc3ac5b723dd62bd9133830ed522", size = 10969164, upload-time = "2026-07-23T19:10:50.271Z" }, + { url = "https://files.pythonhosted.org/packages/42/ce/741cd197496a1abbf51352710fd15ed995d2a2be87189c1da26a450d6e83/ruff-0.16.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e01c21d10eb1b29f47b7454e1f4056db9a3f0260c646aa88457c610291db9f81", size = 10488846, upload-time = "2026-07-23T19:10:52.639Z" }, + { url = "https://files.pythonhosted.org/packages/52/2a/a2db8e88cade358f5cdcb05674a917751074109315d014eb6352d9a893f7/ruff-0.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e364e5ed22ed8dc05082fd78e35308618260907ac2d3c1d637b2e682415b6c9", size = 10889729, upload-time = "2026-07-23T19:10:54.89Z" }, + { url = "https://files.pythonhosted.org/packages/42/65/62a771694ebd63029dc953e27dbad40e1588bd4860ff9fe881018fddaa49/ruff-0.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d327b8fc113a1d4421a04f3839d3752057c8dd1ee320223a6f3f52d04ada462a", size = 10568275, upload-time = "2026-07-23T19:10:56.993Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e2/ced249fe8af5f086c5c58cc21cc3356d50f32f7401c5df87050c999620a7/ruff-0.16.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b50c55e263103586b3dcf5f73d479eb8cb5fdb6098fec59a62891dab653717", size = 11385112, upload-time = "2026-07-23T19:10:59.615Z" }, + { url = "https://files.pythonhosted.org/packages/87/0b/05154977a8fd69eeb6c103271f55403bfd8711f5c0f8ed07489d95a504e7/ruff-0.16.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff4a79ce3ec0172f3241943835de1c4cb4e2dcd07f0f8c2d02603dbbbee4b17", size = 12207008, upload-time = "2026-07-23T19:11:02.154Z" }, + { url = "https://files.pythonhosted.org/packages/fb/29/98225831a3a1eab0e02f4acc6ca6559a98611dcc68b6965ff4b7234627c1/ruff-0.16.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e95c448fca1fb2a18372a9440926c5a6ee789639bb975c72e7ae6d0b04218ab4", size = 11650842, upload-time = "2026-07-23T19:11:04.557Z" }, + { url = "https://files.pythonhosted.org/packages/91/66/6bd3cf90500653d55dc0ffc8507aa8300bd49d0214b2e8cb4d3fef2943ba/ruff-0.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f11a8d11010301d0a398a2fdef67691feca7294da6aef55e2150e8fa2cd520b", size = 11400718, upload-time = "2026-07-23T19:11:09.233Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a2/a54eb4eae05d66364050a5d3b8a9c5ef88196531b3cbe7109d873f87f819/ruff-0.16.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:48044c678e9cb8698246c99b14aaccfa6601dea7379eb48a6f8f73f7a6d86cd0", size = 11426177, upload-time = "2026-07-23T19:11:11.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/be/16e3eea4b2a478a496919f5e36f17c4559e54620bd3bbac5d6affa068006/ruff-0.16.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7aa0959bad8eb8bef50340154fc9b58678dae31fa4293afa38b44b6e552c0213", size = 10856126, upload-time = "2026-07-23T19:11:14.221Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/252eb8b868a16eec7257c14f504f77537e734b2d69c762e639e588e304a3/ruff-0.16.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28ea2b7df8ebf7f9da6b7d47b230ab48f387c0a29be3b474c4d0740e197bb9af", size = 10571208, upload-time = "2026-07-23T19:11:16.378Z" }, + { url = "https://files.pythonhosted.org/packages/21/09/817a482f542f7570cbb4554b26e896610c7114f539b1d9e2d2145bf6bef6/ruff-0.16.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:33a3dfac8c35f81498dea9181bccc2f4c4bc8f1521a1dd9406e77643e0f0fb09", size = 11063329, upload-time = "2026-07-23T19:11:19.173Z" }, + { url = "https://files.pythonhosted.org/packages/2e/23/9403c180ca1cb9b1f7335f5c3e5305c09d49ea5b345196682a36028bde4a/ruff-0.16.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a5237a0bda500d30d81b8e07a6973a5cbc772864cbf746ae2f4e8a2e01c9f4ed", size = 11489751, upload-time = "2026-07-23T19:11:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/b2/1d/1b2ef7bcde851c78d7f17f1cca13fd6dc695fc4b3d6197941e72cae5b132/ruff-0.16.0-py3-none-win32.whl", hash = "sha256:7fab76fa065c873f41ff744347c6e77bcc3dfec4bcc754dc26b63d23c0f7f5fb", size = 10785885, upload-time = "2026-07-23T19:11:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a3/d5e4ef7a56be3f928ffb90b94c25ba7d3cb9c7fe0736aeaaedf361770712/ruff-0.16.0-py3-none-win_amd64.whl", hash = "sha256:429c117f022bf481fabd9d551e7a3952b24c65e6ef44337ea09d90bebef14472", size = 11923141, upload-time = "2026-07-23T19:11:26.409Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9a/8415f2657cbe200f41a4531ccededf135505a92d4a012229121f885b26f9/ruff-0.16.0-py3-none-win_arm64.whl", hash = "sha256:14296fedcd2705c77ab8235439278bbb38f285cf7da5528b00b3e330c3d4872d", size = 11273407, upload-time = "2026-07-23T19:11:28.705Z" }, ] [[package]]