From 41752508d88a347df145ea97a8e2d4e081000ec4 Mon Sep 17 00:00:00 2001 From: schapper Date: Tue, 30 Sep 2025 16:43:15 -0700 Subject: [PATCH 01/20] wip - repackage/modularize --- gers/examples/python/constants.py | 24 +- gers/examples/python/match_classes.py | 164 ++++- gers/examples/python/match_traces.ipynb | 79 ++- gers/examples/python/match_traces.py | 640 ++++++++++++++---- gers/examples/python/route_utils.py | 78 ++- .../python/tests/match_traces_test.py | 38 +- gers/examples/python/tests/test_setup.py | 4 +- gers/examples/python/tests/utils_test.py | 62 +- gers/examples/python/utils.py | 174 +++-- .../schema/addresses/address/models.py | 4 +- .../overture/schema/base/bathymetry/models.py | 8 +- .../schema/base/infrastructure/models.py | 6 +- .../src/overture/schema/base/land/models.py | 6 +- .../overture/schema/base/land_cover/models.py | 8 +- .../overture/schema/base/land_use/models.py | 6 +- .../src/overture/schema/base/water/models.py | 6 +- .../schema/buildings/building/models.py | 6 +- .../schema/buildings/building_part/models.py | 6 +- .../src/overture/schema/core/models.py | 2 +- .../overture-schema-core/tests/test_models.py | 73 +- .../schema/divisions/division/models.py | 6 +- .../schema/divisions/division_area/models.py | 6 +- .../divisions/division_boundary/models.py | 6 +- packages/overture-schema-foundation/README.md | 1 + .../overture-schema-foundation/pyproject.toml | 49 ++ .../src/overture/__init__.py | 1 + .../src/overture/schema/__init__.py | 1 + .../overture/schema/foundation/__about__.py | 1 + .../overture/schema/foundation/__init__.py | 21 + .../schema/foundation/primitive}/geometry.py | 0 .../src/overture/schema/foundation/py.typed | 0 .../tests/primitive}/test_geometry.py | 7 +- .../overture/schema/places/place/models.py | 6 +- .../schema/transportation/connector/models.py | 6 +- .../schema/transportation/segment/models.py | 8 +- .../tests/test_hashability.py | 2 +- uv.lock | 29 + 37 files changed, 1194 insertions(+), 350 deletions(-) create mode 100644 packages/overture-schema-foundation/README.md create mode 100644 packages/overture-schema-foundation/pyproject.toml create mode 100644 packages/overture-schema-foundation/src/overture/__init__.py create mode 100644 packages/overture-schema-foundation/src/overture/schema/__init__.py create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/__about__.py create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/__init__.py rename packages/{overture-schema-core/src/overture/schema/core => overture-schema-foundation/src/overture/schema/foundation/primitive}/geometry.py (100%) create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/py.typed rename packages/{overture-schema-core/tests => overture-schema-foundation/tests/primitive}/test_geometry.py (99%) diff --git a/gers/examples/python/constants.py b/gers/examples/python/constants.py index f15adb2d0..24343934e 100644 --- a/gers/examples/python/constants.py +++ b/gers/examples/python/constants.py @@ -1,27 +1,25 @@ -from enum import Enum -import os DEFAULT_H3_RESOLUTION = 12 # default params for nearest match -DEFAULT_NEAREST_MAX_DISTANCE = 100 # meters +DEFAULT_NEAREST_MAX_DISTANCE = 100 # meters # default params for trace snapping -DEFAULT_SIGMA = 4.1 # 4.10351310622546; -DEFAULT_BETA = 0.9 # 0.905918746744877 -> this default beta was found to apply to a 5 second sample rate. +DEFAULT_SIGMA = 4.1 # 4.10351310622546; +DEFAULT_BETA = 0.9 # 0.905918746744877 -> this default beta was found to apply to a 5 second sample rate. # also was found to have good noise rejection characteristics and performed just as well or better than 1 second data, so it # is now our default sampling period - even if the raw data was sampled at a higher rate -DEFAULT_MAX_POINT_TO_ROAD_DISTANCE = 10 # 200m in original paper -DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE = 300 # what's a good value for this? 2km in original paper but too slow +DEFAULT_MAX_POINT_TO_ROAD_DISTANCE = 10 # 200m in original paper +DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE = ( + 300 # what's a good value for this? 2km in original paper but too slow +) DEFAULT_ALLOW_LOOPS = False -DEFAULT_SEGMENT_REVISIT_PENALTY = 100 # set to 0 if no penalty is desired -DEFAULT_VIA_POINT_PENALTY_WEIGHT = 100 # set to 0 if no penalty is desired -DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE = 60 # seconds -DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE = 300 # meters +DEFAULT_SEGMENT_REVISIT_PENALTY = 100 # set to 0 if no penalty is desired +DEFAULT_VIA_POINT_PENALTY_WEIGHT = 100 # set to 0 if no penalty is desired +DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE = 60 # seconds +DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE = 300 # meters """default column separator of text files""" COLUMN_SEPARATOR = "\t" DATA_DIR = "gers/examples/python/data" - - diff --git a/gers/examples/python/match_classes.py b/gers/examples/python/match_classes.py index 0e1c55805..05bfae56b 100644 --- a/gers/examples/python/match_classes.py +++ b/gers/examples/python/match_classes.py @@ -1,39 +1,67 @@ import json -from typing import Dict, Iterable +from collections.abc import Iterable + +import constants from shapely.geometry import Point from shapely.geometry.base import BaseGeometry -import constants + class MatchableFeature: """ Convenience class to hold an id, a shapely geometry, and optionally a dictionary of properties for use in matching. It can be trivially populated from geojson and overture as an extension of geojson. """ - def __init__(self, id: str, geometry:BaseGeometry, properties: dict=None) -> None: + + def __init__( + self, id: str, geometry: BaseGeometry, properties: dict = None + ) -> None: self.id = str(id) self.geometry = geometry self.properties = properties def __str__(self) -> str: - return json.dumps({ - "id": self.id, - "geometry": self.geometry.wkt, - "properties": self.properties - }) + return json.dumps( + { + "id": self.id, + "geometry": self.geometry.wkt, + "properties": self.properties, + } + ) def get_connector_ids(self) -> Iterable[str]: - return self.properties["connector_ids"] if self.properties is not None and "connector_ids" in self.properties else [] + return ( + self.properties["connector_ids"] + if self.properties is not None and "connector_ids" in self.properties + else [] + ) + class MatchableFeaturesSet: """Collection of matchable features, indexed by id, and by cells (H3 in current implementation)""" - def __init__(self, features: Dict[str, Iterable[MatchableFeature]], cells_by_id: Dict[str, Iterable[str]], features_by_cell: Dict[str, Iterable[MatchableFeature]]) -> None: + + def __init__( + self, + features: dict[str, Iterable[MatchableFeature]], + cells_by_id: dict[str, Iterable[str]], + features_by_cell: dict[str, Iterable[MatchableFeature]], + ) -> None: self.features_by_id = features self.cells_by_id = cells_by_id self.features_by_cell = features_by_cell + class MatchedFeature: """One matched feature with match-relevant information""" - def __init__(self, id: str, matched_feature: MatchableFeature, overlapping_geometry: BaseGeometry, score: float, source_lr: Iterable[float]=None, candidate_lr: Iterable[float]=None) -> None: + + def __init__( + self, + id: str, + matched_feature: MatchableFeature, + overlapping_geometry: BaseGeometry, + score: float, + source_lr: Iterable[float] = None, + candidate_lr: Iterable[float] = None, + ) -> None: """ Attributes: id: the gers id of the matched feature @@ -43,7 +71,7 @@ def __init__(self, id: str, matched_feature: MatchableFeature, overlapping_geome source_lr: the Location Reference in the source geometry of the part that matched as array of from-to points projection factors candidate_lr: the Location Reference in the matched geometry of the part that matched the source geometry """ - self.id = id # the gers id of the matched feature + self.id = id # the gers id of the matched feature self.matched_feature = matched_feature self.overlapping_geometry = overlapping_geometry self.score = score @@ -54,7 +82,9 @@ def to_json(self): j = { "id": str(self.id), "candidate_wkt": self.matched_feature.geometry.wkt, - "overlapping_wkt": self.overlapping_geometry.wkt if self.overlapping_geometry is not None else None, + "overlapping_wkt": self.overlapping_geometry.wkt + if self.overlapping_geometry is not None + else None, "score": self.score, } if self.source_lr is not None: @@ -66,30 +96,38 @@ def to_json(self): def __str__(self) -> str: return json.dumps(self.to_json()) + class TraceSnapOptions: - """"Parameters for matching a trace to road segments""" - def __init__(self, \ - sigma=constants.DEFAULT_SIGMA,\ - beta=constants.DEFAULT_BETA,\ - max_point_to_road_distance=constants.DEFAULT_MAX_POINT_TO_ROAD_DISTANCE,\ - max_route_to_trace_distance_difference=constants.DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE,\ - allow_loops=constants.DEFAULT_ALLOW_LOOPS, - revisit_segment_penalty_weight=constants.DEFAULT_SEGMENT_REVISIT_PENALTY, - revisit_via_point_penalty_weight=constants.DEFAULT_VIA_POINT_PENALTY_WEIGHT, - broken_time_gap_reset_sequence=constants.DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE, - broken_distance_gap_reset_sequence=constants.DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE) -> None: + """ "Parameters for matching a trace to road segments""" + + def __init__( + self, + sigma=constants.DEFAULT_SIGMA, + beta=constants.DEFAULT_BETA, + max_point_to_road_distance=constants.DEFAULT_MAX_POINT_TO_ROAD_DISTANCE, + max_route_to_trace_distance_difference=constants.DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE, + allow_loops=constants.DEFAULT_ALLOW_LOOPS, + revisit_segment_penalty_weight=constants.DEFAULT_SEGMENT_REVISIT_PENALTY, + revisit_via_point_penalty_weight=constants.DEFAULT_VIA_POINT_PENALTY_WEIGHT, + broken_time_gap_reset_sequence=constants.DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE, + broken_distance_gap_reset_sequence=constants.DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE, + ) -> None: self.sigma = sigma self.beta = beta self.allow_loops = allow_loops self.max_point_to_road_distance = max_point_to_road_distance - self.max_route_to_trace_distance_difference = max_route_to_trace_distance_difference + self.max_route_to_trace_distance_difference = ( + max_route_to_trace_distance_difference + ) self.revisit_segment_penalty_weight = revisit_segment_penalty_weight self.revisit_via_point_penalty_weight = revisit_via_point_penalty_weight self.broken_time_gap_reset_sequence = broken_time_gap_reset_sequence self.broken_distance_gap_reset_sequence = broken_distance_gap_reset_sequence + class RouteStep: """One step in a route, corresponding to one road segment feature""" + def __init__(self, feature: MatchableFeature, via_point: Point) -> None: """ Attributes: @@ -99,15 +137,34 @@ def __init__(self, feature: MatchableFeature, via_point: Point) -> None: self.feature = feature self.via_point = via_point + class Route: """A route, consisting of a sequence of steps""" + def __init__(self, distance: float, steps: Iterable[RouteStep]) -> None: self.distance = distance self.steps = steps + class SnappedPointPrediction: """A road segment feature as a snap prediction for point in a trace, with relevant match signals""" - def __init__(self, id: str, snapped_point: Point, referenced_feature: MatchableFeature, distance_to_snapped_road: float, route_distance_to_prev_point: float, emission_prob: float, best_transition_prob: float, best_log_prob: float, best_prev_prediction: float, best_sequence: Iterable[str], best_route_via_points: Iterable[str], best_revisited_via_points_count:int, best_revisited_segments_count:int) -> None: + + def __init__( + self, + id: str, + snapped_point: Point, + referenced_feature: MatchableFeature, + distance_to_snapped_road: float, + route_distance_to_prev_point: float, + emission_prob: float, + best_transition_prob: float, + best_log_prob: float, + best_prev_prediction: float, + best_sequence: Iterable[str], + best_route_via_points: Iterable[str], + best_revisited_via_points_count: int, + best_revisited_segments_count: int, + ) -> None: self.id = str(id) self.snapped_point = snapped_point self.referenced_feature = referenced_feature @@ -146,9 +203,18 @@ def to_json(self, diagnostic_mode=False): return j + class PointSnapInfo: """Snap-to-road match information corresponding to one point in a trace""" - def __init__(self, index: int, original_point: Point, time: str, seconds_since_prev_point: float=None, predictions:Iterable[SnappedPointPrediction]=[]) -> None: + + def __init__( + self, + index: int, + original_point: Point, + time: str, + seconds_since_prev_point: float = None, + predictions: Iterable[SnappedPointPrediction] = [], + ) -> None: self.index = index self.original_point = original_point self.time = time @@ -157,8 +223,16 @@ def __init__(self, index: int, original_point: Point, time: str, seconds_since_p self.best_prediction = None self.ignore = False - def to_json(self, diagnostic_mode: bool=False, include_all_predictions: bool=False,): - best_prediction_json = None if self.best_prediction is None else self.best_prediction.to_json(diagnostic_mode) + def to_json( + self, + diagnostic_mode: bool = False, + include_all_predictions: bool = False, + ): + best_prediction_json = ( + None + if self.best_prediction is None + else self.best_prediction.to_json(diagnostic_mode) + ) j = { "original_point": self.original_point.wkt, @@ -174,12 +248,31 @@ def to_json(self, diagnostic_mode: bool=False, include_all_predictions: bool=Fal j["point_index"] = self.index if include_all_predictions: - j["predictions"] = list(map(lambda x: x.to_json(diagnostic_mode), self.predictions)) + j["predictions"] = list( + map(lambda x: x.to_json(diagnostic_mode), self.predictions) + ) return j + class TraceMatchResult: """Result of a matching trace to road segments""" - def __init__(self, id: str, source_wkt: str, points: Iterable[PointSnapInfo], source_length: float, target_candidates_count: int, matched_target_ids: Iterable[str]=None, elapsed: float=None, sequence_breaks: int=0, points_with_matches: int=0, route_length: float=0, avg_dist_to_road: float=None, revisited_via_points: int=0, revisited_segments: int=0) -> None: + + def __init__( + self, + id: str, + source_wkt: str, + points: Iterable[PointSnapInfo], + source_length: float, + target_candidates_count: int, + matched_target_ids: Iterable[str] = None, + elapsed: float = None, + sequence_breaks: int = 0, + points_with_matches: int = 0, + route_length: float = 0, + avg_dist_to_road: float = None, + revisited_via_points: int = 0, + revisited_segments: int = 0, + ) -> None: self.id = id self.source_wkt = source_wkt self.points = points @@ -195,7 +288,12 @@ def __init__(self, id: str, source_wkt: str, points: Iterable[PointSnapInfo], so self.revisited_segments = revisited_segments def to_json(self, diagnostic_mode=False, include_all_predictions=False): - points_json = list(map(lambda x: x.to_json(diagnostic_mode, include_all_predictions), self.points)) + points_json = list( + map( + lambda x: x.to_json(diagnostic_mode, include_all_predictions), + self.points, + ) + ) return { "id": str(self.id), "elapsed": self.elapsed, @@ -209,7 +307,7 @@ def to_json(self, diagnostic_mode=False, include_all_predictions=False): "revisited_segments": self.revisited_segments, "target_candidates_count": self.target_candidates_count, "target_ids": self.matched_target_ids, - "points": points_json + "points": points_json, } def __str__(self) -> str: diff --git a/gers/examples/python/match_traces.ipynb b/gers/examples/python/match_traces.ipynb index 413f761fd..8f8618a0a 100644 --- a/gers/examples/python/match_traces.ipynb +++ b/gers/examples/python/match_traces.ipynb @@ -158,18 +158,21 @@ } ], "source": [ - "\n", "to_match_gdf = gpd.read_file(input_to_match_file)\n", "to_match_gdf.crs = \"epsg:4326\"\n", "\n", "# add column \"id_to_match\"; this will be used to group all match candidates for a feature, so it must be unique within your data set.\n", "# if your data doesn't have an id column, you can use to_match_gdf.index for example\n", - "to_match_gdf[\"id_to_match\"] = to_match_gdf[\"id\"] \n", + "to_match_gdf[\"id_to_match\"] = to_match_gdf[\"id\"]\n", "property_columns = to_match_gdf.columns.difference([\"geometry\", \"id\", \"type\"])\n", - "to_match_gdf[\"properties\"] = to_match_gdf[property_columns].apply(lambda x: x.to_dict(), axis=1)\n", + "to_match_gdf[\"properties\"] = to_match_gdf[property_columns].apply(\n", + " lambda x: x.to_dict(), axis=1\n", + ")\n", "\n", "# construct a MatchableFeature object for each feature\n", - "to_match_gdf[\"feature_to_match\"] = to_match_gdf.apply(lambda row: MatchableFeature(row.id, row.geometry, row.properties), axis=1)\n", + "to_match_gdf[\"feature_to_match\"] = to_match_gdf.apply(\n", + " lambda row: MatchableFeature(row.id, row.geometry, row.properties), axis=1\n", + ")\n", "\n", "to_match_gdf.head(3)" ] @@ -297,12 +300,16 @@ "overture_gdf = gpd.read_file(input_overture_file)\n", "overture_gdf.crs = \"epsg:4326\"\n", "\n", - "# combine properties into a single column \n", + "# combine properties into a single column\n", "property_columns = overture_gdf.columns.difference([\"geometry\", \"id\", \"type\"])\n", - "overture_gdf[\"properties\"] = overture_gdf[property_columns].apply(lambda x: x.to_dict(), axis=1)\n", + "overture_gdf[\"properties\"] = overture_gdf[property_columns].apply(\n", + " lambda x: x.to_dict(), axis=1\n", + ")\n", "\n", "# construct a MatchableFeature object for each feature\n", - "overture_gdf[\"candidate_feature\"] = overture_gdf.apply(lambda row: MatchableFeature(row.id, row.geometry, row.properties), axis=1)\n", + "overture_gdf[\"candidate_feature\"] = overture_gdf.apply(\n", + " lambda row: MatchableFeature(row.id, row.geometry, row.properties), axis=1\n", + ")\n", "\n", "overture_gdf.head(3)" ] @@ -510,10 +517,14 @@ "to_match_utm_gdf[\"original_geometry\"] = to_match_utm_gdf[\"geometry\"].copy(deep=True)\n", "\n", "# add 20 meters buffer to the features to match, to account for the fact that the overture features are not perfectly aligned with the features to be matched\n", - "to_match_utm_gdf[\"geometry\"] = to_match_utm_gdf[\"original_geometry\"].buffer(20, cap_style=2)\n", + "to_match_utm_gdf[\"geometry\"] = to_match_utm_gdf[\"original_geometry\"].buffer(\n", + " 20, cap_style=2\n", + ")\n", "\n", "# spatial join between points and segments - get nearest overture feature, where distance < 100 meters\n", - "joined_gdf = sjoin(to_match_utm_gdf, candidates_utm_gdf, how=\"left\", predicate=\"intersects\")\n", + "joined_gdf = sjoin(\n", + " to_match_utm_gdf, candidates_utm_gdf, how=\"left\", predicate=\"intersects\"\n", + ")\n", "joined_gdf.head(3)" ] }, @@ -590,7 +601,9 @@ ], "source": [ "# group join results by id_to_match, and aggregate the candidate features into a list\n", - "grouped_df = joined_gdf.groupby([\"id_to_match\"]).agg({\"feature_to_match\": \"first\", \"candidate_feature\": lambda x: list(x)})\n", + "grouped_df = joined_gdf.groupby([\"id_to_match\"]).agg(\n", + " {\"feature_to_match\": \"first\", \"candidate_feature\": lambda x: list(x)}\n", + ")\n", "grouped_df = grouped_df.reset_index()\n", "grouped_df.head(3)" ] @@ -677,8 +690,11 @@ ], "source": [ "# run the trace matching algorithm for each trace feature to match\n", - "options = TraceSnapOptions(max_point_to_road_distance=10) \n", - "grouped_df[\"match_result\"] = grouped_df.apply(lambda row: get_trace_matches(row.feature_to_match, row.candidate_feature, options), axis=1)\n", + "options = TraceSnapOptions(max_point_to_road_distance=10)\n", + "grouped_df[\"match_result\"] = grouped_df.apply(\n", + " lambda row: get_trace_matches(row.feature_to_match, row.candidate_feature, options),\n", + " axis=1,\n", + ")\n", "grouped_df.head(3)" ] }, @@ -690,8 +706,9 @@ "source": [ "# save the results to a file, result as one json line per feature to match\n", "import numpy as np\n", + "\n", "results_df = grouped_df.apply(lambda x: x.match_result.to_json(), axis=1)\n", - "np.savetxt(output_file, results_df.values, fmt='%s')" + "np.savetxt(output_file, results_df.values, fmt=\"%s\")" ] }, { @@ -835,16 +852,34 @@ ], "source": [ "# add some of the metrics from the result object as columns for analysis\n", - "grouped_df[\"source_length\"] = grouped_df.apply(lambda x: x.match_result.source_length, axis=1)\n", - "grouped_df[\"route_length\"] = grouped_df.apply(lambda x: x.match_result.route_length, axis=1)\n", + "grouped_df[\"source_length\"] = grouped_df.apply(\n", + " lambda x: x.match_result.source_length, axis=1\n", + ")\n", + "grouped_df[\"route_length\"] = grouped_df.apply(\n", + " lambda x: x.match_result.route_length, axis=1\n", + ")\n", "grouped_df[\"points\"] = grouped_df.apply(lambda x: len(x.match_result.points), axis=1)\n", - "grouped_df[\"points_with_matches\"] = grouped_df.apply(lambda x: x.match_result.points_with_matches, axis=1)\n", - "grouped_df[\"avg_dist_to_road\"] = grouped_df.apply(lambda x: x.match_result.avg_dist_to_road, axis=1)\n", - "grouped_df[\"sequence_breaks\"] = grouped_df.apply(lambda x: x.match_result.sequence_breaks, axis=1)\n", - "grouped_df[\"revisited_via_points\"] = grouped_df.apply(lambda x: x.match_result.revisited_via_points, axis=1)\n", - "grouped_df[\"revisited_segments\"] = grouped_df.apply(lambda x: x.match_result.revisited_segments, axis=1)\n", - "grouped_df[\"candidates_count\"] = grouped_df.apply(lambda x: x.match_result.target_candidates_count, axis=1)\n", - "grouped_df[\"matched_segments\"] = grouped_df.apply(lambda x: len(x.match_result.matched_target_ids), axis=1)\n", + "grouped_df[\"points_with_matches\"] = grouped_df.apply(\n", + " lambda x: x.match_result.points_with_matches, axis=1\n", + ")\n", + "grouped_df[\"avg_dist_to_road\"] = grouped_df.apply(\n", + " lambda x: x.match_result.avg_dist_to_road, axis=1\n", + ")\n", + "grouped_df[\"sequence_breaks\"] = grouped_df.apply(\n", + " lambda x: x.match_result.sequence_breaks, axis=1\n", + ")\n", + "grouped_df[\"revisited_via_points\"] = grouped_df.apply(\n", + " lambda x: x.match_result.revisited_via_points, axis=1\n", + ")\n", + "grouped_df[\"revisited_segments\"] = grouped_df.apply(\n", + " lambda x: x.match_result.revisited_segments, axis=1\n", + ")\n", + "grouped_df[\"candidates_count\"] = grouped_df.apply(\n", + " lambda x: x.match_result.target_candidates_count, axis=1\n", + ")\n", + "grouped_df[\"matched_segments\"] = grouped_df.apply(\n", + " lambda x: len(x.match_result.matched_target_ids), axis=1\n", + ")\n", "grouped_df[\"elapsed\"] = grouped_df.apply(lambda x: x.match_result.elapsed, axis=1)\n", "grouped_df.head(3)" ] diff --git a/gers/examples/python/match_traces.py b/gers/examples/python/match_traces.py index bbf2e6a02..8b55cde97 100644 --- a/gers/examples/python/match_traces.py +++ b/gers/examples/python/match_traces.py @@ -1,25 +1,40 @@ import argparse import csv import json -import os import math +import os +from collections.abc import Iterable +from timeit import default_timer as timer import constants +from match_classes import ( + MatchableFeature, + PointSnapInfo, + RouteStep, + SnappedPointPrediction, + TraceMatchResult, + TraceSnapOptions, +) from route_utils import get_shortest_route -from match_classes import TraceSnapOptions, MatchableFeature, TraceMatchResult, SnappedPointPrediction, PointSnapInfo, RouteStep -from utils import get_features_with_cells, get_seconds_elapsed, get_distance, get_linestring_length, load_matchable_set - from shapely import Point from shapely.ops import nearest_points -from timeit import default_timer as timer -from typing import Dict, Iterable - -def get_feature_id_to_connected_features(features_overture: Iterable[MatchableFeature]) -> Dict[str, Iterable[MatchableFeature]]: +from utils import ( + get_distance, + get_features_with_cells, + get_linestring_length, + get_seconds_elapsed, + load_matchable_set, +) + + +def get_feature_id_to_connected_features( + features_overture: Iterable[MatchableFeature], +) -> dict[str, Iterable[MatchableFeature]]: """returns a connected roads "graph" as a dictionary of feature id to features that are connected to it, as modeled in overture schema via connector_ids property""" connector_id_to_features = {} for feature in features_overture: for connector_id in feature.get_connector_ids(): - if not connector_id in connector_id_to_features: + if connector_id not in connector_id_to_features: connector_id_to_features[connector_id] = [] connector_id_to_features[connector_id].append(feature) @@ -32,36 +47,56 @@ def get_feature_id_to_connected_features(features_overture: Iterable[MatchableFe feature_id_to_connected_features[feature.id].append(other_feature) return feature_id_to_connected_features + def read_predictions(predictions_file: str): """reads snap predictions from tab separated file with columns: trace_id, point_index, gers_id, score""" p = {} - with open(predictions_file, 'r') as file: + with open(predictions_file) as file: reader = csv.reader(file, delimiter=constants.COLUMN_SEPARATOR) for row in reader: try: trace_id = row[0] point_index = int(row[1]) gers_id = row[3] - if not(trace_id in p): + if trace_id not in p: p[trace_id] = {} p[trace_id][point_index] = gers_id except ValueError: - continue # header or invalid line + continue # header or invalid line return p -def calculate_error_rate(labeled_file: str, target_features_by_id: Dict[str, Iterable[MatchableFeature]], match_results: Iterable[TraceMatchResult]): + +def calculate_error_rate( + labeled_file: str, + target_features_by_id: dict[str, Iterable[MatchableFeature]], + match_results: Iterable[TraceMatchResult], +): """returns total error rate from a labeled file and a list of trace match results""" - if not(os.path.exists(labeled_file)): - print(f'no metrics to compute (file {labeled_file} does not exist)') + if not (os.path.exists(labeled_file)): + print(f"no metrics to compute (file {labeled_file} does not exist)") return labels = read_predictions(labeled_file) total_correct_distance = 0 total_incorrect_distance = 0 - with open(labeled_file + ".actual.txt",'w') as f: - f.write(constants.COLUMN_SEPARATOR.join(["trace_id", "point_index", "label_gers_id", "prediction_gers_id", "label_snapped_wkt", "prediction_snapped_wkt", "distance_to_prev_point", "is_correct"]) + "\n") + with open(labeled_file + ".actual.txt", "w") as f: + f.write( + constants.COLUMN_SEPARATOR.join( + [ + "trace_id", + "point_index", + "label_gers_id", + "prediction_gers_id", + "label_snapped_wkt", + "prediction_snapped_wkt", + "distance_to_prev_point", + "is_correct", + ] + ) + + "\n" + ) for trace_match_result in match_results: - if not(trace_match_result.id in labels): + if trace_match_result.id not in labels: continue correct_distance = 0 @@ -69,13 +104,17 @@ def calculate_error_rate(labeled_file: str, target_features_by_id: Dict[str, Ite prev_point = None for point in trace_match_result.points: - if not(point.index in labels[trace_match_result.id]): - print(f'no label for trace_id={trace_match_result.id} point_index={point.index}') + if point.index not in labels[trace_match_result.id]: + print( + f"no label for trace_id={trace_match_result.id} point_index={point.index}" + ) break label_gers_id = labels[trace_match_result.id][point.index] dist_to_prev_point = 0 - is_correct = not(point.best_prediction is None) and (str(point.best_prediction.id) == label_gers_id) + is_correct = point.best_prediction is not None and ( + str(point.best_prediction.id) == label_gers_id + ) if prev_point is not None: dist_to_prev_point = get_distance(prev_point, point.original_point) correct_distance += dist_to_prev_point @@ -88,78 +127,124 @@ def calculate_error_rate(labeled_file: str, target_features_by_id: Dict[str, Ite incorrect_distance += dist_to_prev_point label_snapped_point = None - if not(label_gers_id in target_features_by_id): - print(f'no target feature for label_gers_id={label_gers_id}') + if label_gers_id not in target_features_by_id: + print(f"no target feature for label_gers_id={label_gers_id}") else: label_shape = target_features_by_id[label_gers_id].geometry - x, label_snapped_point = nearest_points(point.original_point, label_shape) - - columns = [\ - str(trace_match_result.id), \ - str(point.index), \ - str(label_gers_id), \ - str(point.best_prediction.id) if point.best_prediction is not None else "", \ - label_snapped_point.wkt if label_snapped_point is not None else "", \ - point.best_prediction.snapped_point.wkt if point.best_prediction is not None else "", \ - str(dist_to_prev_point), \ - str(is_correct), \ - ] + x, label_snapped_point = nearest_points( + point.original_point, label_shape + ) + + columns = [ + str(trace_match_result.id), + str(point.index), + str(label_gers_id), + str(point.best_prediction.id) + if point.best_prediction is not None + else "", + label_snapped_point.wkt if label_snapped_point is not None else "", + point.best_prediction.snapped_point.wkt + if point.best_prediction is not None + else "", + str(dist_to_prev_point), + str(is_correct), + ] f.write(constants.COLUMN_SEPARATOR.join(columns) + "\n") prev_point = point.original_point trace_error_rate = incorrect_distance / correct_distance - print(rf"trace_id={trace_match_result.id} trace_error_rate={trace_error_rate:.2f} correct_distance={correct_distance:.2f} incorrect_distance={incorrect_distance:.2f}") + print( + rf"trace_id={trace_match_result.id} trace_error_rate={trace_error_rate:.2f} correct_distance={correct_distance:.2f} incorrect_distance={incorrect_distance:.2f}" + ) total_correct_distance += correct_distance total_incorrect_distance += incorrect_distance if total_correct_distance == 0: - print('no correct distance') + print("no correct distance") return -1 total_error_rate = total_incorrect_distance / total_correct_distance - print(rf"total_error_rate={total_error_rate:.2f} total_correct_distance={total_correct_distance:.2f} total_incorrect_distance={total_incorrect_distance:.2f}") + print( + rf"total_error_rate={total_error_rate:.2f} total_correct_distance={total_correct_distance:.2f} total_incorrect_distance={total_incorrect_distance:.2f}" + ) return total_error_rate -def output_trace_snap_results(match_results: Iterable[TraceMatchResult], output_file_name: str, output_for_judgment: bool = False): - results_json = list(map(lambda x: x.to_json(diagnostic_mode=False, include_all_predictions=False), match_results)) - with open(output_file_name, 'w') as f: + +def output_trace_snap_results( + match_results: Iterable[TraceMatchResult], + output_file_name: str, + output_for_judgment: bool = False, +): + results_json = list( + map( + lambda x: x.to_json(diagnostic_mode=False, include_all_predictions=False), + match_results, + ) + ) + with open(output_file_name, "w") as f: json.dump(results_json, f, indent=4) - results_json = list(map(lambda x: x.to_json(diagnostic_mode=True, include_all_predictions=False), match_results)) - with open(output_file_name + ".with_diagnostics.json", 'w') as f: + results_json = list( + map( + lambda x: x.to_json(diagnostic_mode=True, include_all_predictions=False), + match_results, + ) + ) + with open(output_file_name + ".with_diagnostics.json", "w") as f: json.dump(results_json, f, indent=4) - results_json = list(map(lambda x: x.to_json(diagnostic_mode=True, include_all_predictions=True), match_results)) - with open(output_file_name + ".with_diagnostics-all-predictions.json", 'w') as f: + results_json = list( + map( + lambda x: x.to_json(diagnostic_mode=True, include_all_predictions=True), + match_results, + ) + ) + with open(output_file_name + ".with_diagnostics-all-predictions.json", "w") as f: json.dump(results_json, f, indent=4) if output_for_judgment: - with open(output_file_name + ".for_judgment.txt",'w') as f: - f.write(constants.COLUMN_SEPARATOR.join(["trace_id", "point_index", "trace_point_wkt", "gers_id"]) + "\n") + with open(output_file_name + ".for_judgment.txt", "w") as f: + f.write( + constants.COLUMN_SEPARATOR.join( + ["trace_id", "point_index", "trace_point_wkt", "gers_id"] + ) + + "\n" + ) for r in match_results: for idx, p in enumerate(r.points): columns = [ str(r.id), str(idx), p.original_point.wkt, - str(p.best_prediction.id) if p.best_prediction is not None else "" + str(p.best_prediction.id) + if p.best_prediction is not None + else "", ] f.write(constants.COLUMN_SEPARATOR.join(columns) + "\n") - with open(output_file_name + ".snapped_points.txt",'w') as f: - f.write(constants.COLUMN_SEPARATOR.join(["trace_id", "point_index", "gers_id", "snapped_point_wkt"]) + "\n") + with open(output_file_name + ".snapped_points.txt", "w") as f: + f.write( + constants.COLUMN_SEPARATOR.join( + ["trace_id", "point_index", "gers_id", "snapped_point_wkt"] + ) + + "\n" + ) for r in match_results: for idx, p in enumerate(r.points): columns = [ str(r.id), str(idx), - str(p.best_prediction.id) if p.best_prediction is not None else "", - p.best_prediction.snapped_point.wkt if p.best_prediction is not None else "" + str(p.best_prediction.id) + if p.best_prediction is not None + else "", + p.best_prediction.snapped_point.wkt + if p.best_prediction is not None + else "", ] f.write(constants.COLUMN_SEPARATOR.join(columns) + "\n") - with open(output_file_name + ".auto_metrics.txt",'w') as f: + with open(output_file_name + ".auto_metrics.txt", "w") as f: header = [ "id", "source_length", @@ -174,7 +259,7 @@ def output_trace_snap_results(match_results: Iterable[TraceMatchResult], output_ "revisited_via_points", "revisited_segments", "elapsed", - "source_wkt" + "source_wkt", ] f.write(constants.COLUMN_SEPARATOR.join(header) + "\n") for r in match_results: @@ -184,7 +269,7 @@ def output_trace_snap_results(match_results: Iterable[TraceMatchResult], output_ str(r.route_length), str(len(r.points)), str(r.points_with_matches), - rf"{(100*r.points_with_matches/len(r.points)):.2f}", + rf"{(100 * r.points_with_matches / len(r.points)):.2f}", str(r.target_candidates_count), str(len(r.matched_target_ids)), str(r.avg_dist_to_road), @@ -196,31 +281,51 @@ def output_trace_snap_results(match_results: Iterable[TraceMatchResult], output_ ] f.write(constants.COLUMN_SEPARATOR.join(columns) + "\n") + def set_best_path_predictions(points: Iterable[PointSnapInfo]): """Sets the best prediction for each point in the sequence, starting from the end and going backwards following the best_prev_prediction chain""" last_point = points[-1] - if last_point.predictions is None or len(last_point.predictions) == 0 or last_point.predictions[0].best_log_prob == 0: - return # no path found - - last_point.best_prediction = last_point.predictions[0] # this is sorted descending by probability, so the first one is the best - for idx in range(len(points)-2, -1, -1): + if ( + last_point.predictions is None + or len(last_point.predictions) == 0 + or last_point.predictions[0].best_log_prob == 0 + ): + return # no path found + + last_point.best_prediction = last_point.predictions[ + 0 + ] # this is sorted descending by probability, so the first one is the best + for idx in range(len(points) - 2, -1, -1): if points[idx + 1].best_prediction is not None: - points[idx].best_prediction = points[idx + 1].best_prediction.best_prev_prediction + points[idx].best_prediction = points[ + idx + 1 + ].best_prediction.best_prev_prediction else: - if not(points[idx].ignore) and len(points[idx].predictions) > 0: + if not (points[idx].ignore) and len(points[idx].predictions) > 0: points[idx].best_prediction = points[idx].predictions[0] -def extend_sequence(steps: Iterable[RouteStep], prev_prediction: SnappedPointPrediction): + +def extend_sequence( + steps: Iterable[RouteStep], prev_prediction: SnappedPointPrediction +): """Extends the sequence of the traveled segments up to the previous point with the new steps; also returns the number of revisited segments and via points""" revisited_via_points_count = 0 revisited_segments_count = 0 - extended_sequence = prev_prediction.best_sequence.copy() if prev_prediction.best_sequence is not None else [] + extended_sequence = ( + prev_prediction.best_sequence.copy() + if prev_prediction.best_sequence is not None + else [] + ) revisited_segments_count = 0 added_via_points = [] for step in steps: - if len(extended_sequence) == 0 or step.feature.id != extended_sequence[-1]: # either first step or new feature - if len(extended_sequence) > 0 and step.feature.id in extended_sequence: # different than prev segment but present in the sequence, so we are revisiting it + if ( + len(extended_sequence) == 0 or step.feature.id != extended_sequence[-1] + ): # either first step or new feature + if ( + len(extended_sequence) > 0 and step.feature.id in extended_sequence + ): # different than prev segment but present in the sequence, so we are revisiting it revisited_segments_count += 1 extended_sequence.append(step.feature.id) if step.via_point is not None: @@ -234,7 +339,7 @@ def extend_sequence(steps: Iterable[RouteStep], prev_prediction: SnappedPointPre for vp in p.best_route_via_points: all_prev_via_points.add(vp) if len(all_prev_via_points) > 100: - break # optimization for very long traces, don't need to check all of them, just the recent ones + break # optimization for very long traces, don't need to check all of them, just the recent ones p = p.best_prev_prediction for added_via_point in added_via_points: @@ -242,20 +347,26 @@ def extend_sequence(steps: Iterable[RouteStep], prev_prediction: SnappedPointPre revisited_via_points_count += 1 return (extended_sequence, revisited_segments_count, revisited_via_points_count) -def get_trace_matches(source_feature: MatchableFeature, target_candidates: Iterable[MatchableFeature], options: TraceSnapOptions) -> TraceMatchResult: + +def get_trace_matches( + source_feature: MatchableFeature, + target_candidates: Iterable[MatchableFeature], + options: TraceSnapOptions, +) -> TraceMatchResult: """Matches a `source_feature` trace to most likely traveled `targe_candidates` road segments""" start = timer() - feature_id_to_connected_features = get_feature_id_to_connected_features(target_candidates) + feature_id_to_connected_features = get_feature_id_to_connected_features( + target_candidates + ) filter_feature_ids = set(map(lambda x: x.id, target_candidates)) - times = source_feature.properties.get('times') + times = source_feature.properties.get("times") points = [] prev_point = None sequence_breaks = 0 for idx, coord in enumerate(source_feature.geometry.coords): - original_point = Point(coord[0], coord[1]) predictions = [] @@ -265,7 +376,10 @@ def get_trace_matches(source_feature: MatchableFeature, target_candidates: Itera if distance_to_road > options.max_point_to_road_distance: continue - emission_prob = (1 / (math.sqrt(2*math.pi) * options.sigma)) * math.exp(-0.5 * ((distance_to_road/options.sigma)**2)) # measurement probability - if was on this road how likely is it to have measured the point at this distance + emission_prob = ( + (1 / (math.sqrt(2 * math.pi) * options.sigma)) + * math.exp(-0.5 * ((distance_to_road / options.sigma) ** 2)) + ) # measurement probability - if was on this road how likely is it to have measured the point at this distance best_log_prob = None best_transition_prob = None best_prev_prediction = None @@ -281,32 +395,67 @@ def get_trace_matches(source_feature: MatchableFeature, target_candidates: Itera best_transition_prob = 1 best_sequence = [target_feature.id] else: - trace_dist_from_prev_point = get_distance(original_point, prev_point.original_point) + trace_dist_from_prev_point = get_distance( + original_point, prev_point.original_point + ) for prev_prediction in prev_point.predictions: - if not(options.allow_loops) and not(prev_prediction.best_sequence is None) and target_feature.id in prev_prediction.best_sequence and prev_prediction.referenced_feature.id != target_feature.id: + if ( + not (options.allow_loops) + and prev_prediction.best_sequence is not None + and target_feature.id in prev_prediction.best_sequence + and prev_prediction.referenced_feature.id != target_feature.id + ): # already part of best sequence, but then moved to a different segment, so this is not a good candidate, it means this would walk back on itself continue - route = get_shortest_route(target_candidates, feature_id_to_connected_features, prev_prediction.referenced_feature, target_feature, prev_prediction.snapped_point, snapped_point, filter_feature_ids, [] if options.allow_loops else prev_prediction.best_sequence) + route = get_shortest_route( + target_candidates, + feature_id_to_connected_features, + prev_prediction.referenced_feature, + target_feature, + prev_prediction.snapped_point, + snapped_point, + filter_feature_ids, + [] if options.allow_loops else prev_prediction.best_sequence, + ) # check distance is not float('inf') - if route is None or route.distance == float('inf') : + if route is None or route.distance == float("inf"): # couldn't find path, skip this prev_match as impossible to transition from it to this match continue dist_diff = abs(trace_dist_from_prev_point - route.distance) - transition_prob = (1 / options.beta) * math.exp(-dist_diff / options.beta) - - extended_sequence, revisited_segments_count, revisited_via_points_count = extend_sequence(route.steps, prev_prediction) - transition_prob *= math.exp(-revisited_via_points_count * options.revisit_via_point_penalty_weight) # todo: what's the right way to penalize revisiting via points? - transition_prob *= math.exp(-revisited_segments_count * options.revisit_segment_penalty_weight) # todo: what's the right way to penalize revisiting segments? - - if dist_diff > options.max_route_to_trace_distance_difference or transition_prob <= 0: + transition_prob = (1 / options.beta) * math.exp( + -dist_diff / options.beta + ) + + ( + extended_sequence, + revisited_segments_count, + revisited_via_points_count, + ) = extend_sequence(route.steps, prev_prediction) + transition_prob *= math.exp( + -revisited_via_points_count + * options.revisit_via_point_penalty_weight + ) # todo: what's the right way to penalize revisiting via points? + transition_prob *= math.exp( + -revisited_segments_count + * options.revisit_segment_penalty_weight + ) # todo: what's the right way to penalize revisiting segments? + + if ( + dist_diff > options.max_route_to_trace_distance_difference + or transition_prob <= 0 + ): continue - #match_prob = prev_prediction.best_prob * emission_prob * transition_prob + # match_prob = prev_prediction.best_prob * emission_prob * transition_prob # probabilities multiplied over many points go to zero (floating point underflow), so use log of product is sum of logs - match_log_prob = prev_prediction.best_log_prob + math.log(emission_prob) + math.log(transition_prob) - #print(f'point#{idx} prev_prediction={prev_prediction.id} transition_prob={transition_prob} emission_prob={emission_prob} match_prob={match_prob} route_dist_from_prev_point={route_dist_from_prev_point} trace_dist_from_prev_point={trace_dist_from_prev_point} dist_diff={dist_diff}') + match_log_prob = ( + prev_prediction.best_log_prob + + math.log(emission_prob) + + math.log(transition_prob) + ) + # print(f'point#{idx} prev_prediction={prev_prediction.id} transition_prob={transition_prob} emission_prob={emission_prob} match_prob={match_prob} route_dist_from_prev_point={route_dist_from_prev_point} trace_dist_from_prev_point={trace_dist_from_prev_point} dist_diff={dist_diff}') if best_log_prob is None or match_log_prob > best_log_prob: best_log_prob = match_log_prob best_transition_prob = transition_prob @@ -322,20 +471,42 @@ def get_trace_matches(source_feature: MatchableFeature, target_candidates: Itera # todo: also include the intermediate features in route.path if best_log_prob is None: - continue # couldn't find a path to this point, skip it - #print(f'point#{idx} candidate feature={target_feature.id} best_log_prob={best_log_prob} best_prev_point={best_prev_prediction.id if best_prev_prediction is not None else None} best_transition_prob={best_transition_prob} emission_prob={emission_prob} distance_to_road={distance_to_road}') - prediction = SnappedPointPrediction(target_feature.id, snapped_point, target_feature, distance_to_road, best_route_dist_from_prev_point, emission_prob, best_transition_prob, best_log_prob, best_prev_prediction, best_sequence, best_route_via_points, best_revisited_via_points_count, best_revisited_segments_count) + continue # couldn't find a path to this point, skip it + # print(f'point#{idx} candidate feature={target_feature.id} best_log_prob={best_log_prob} best_prev_point={best_prev_prediction.id if best_prev_prediction is not None else None} best_transition_prob={best_transition_prob} emission_prob={emission_prob} distance_to_road={distance_to_road}') + prediction = SnappedPointPrediction( + target_feature.id, + snapped_point, + target_feature, + distance_to_road, + best_route_dist_from_prev_point, + emission_prob, + best_transition_prob, + best_log_prob, + best_prev_prediction, + best_sequence, + best_route_via_points, + best_revisited_via_points_count, + best_revisited_segments_count, + ) predictions.append(prediction) predictions.sort(key=lambda x: x.best_log_prob, reverse=True) - time_since_prev_point = None if times is None or prev_point is None else get_seconds_elapsed(times[prev_point.index], times[idx]) + time_since_prev_point = ( + None + if times is None or prev_point is None + else get_seconds_elapsed(times[prev_point.index], times[idx]) + ) time = None if times is None else times[idx] - point = PointSnapInfo(idx, original_point, time, time_since_prev_point, predictions) + point = PointSnapInfo( + idx, original_point, time, time_since_prev_point, predictions + ) points.append(point) if len(predictions) > 0: - prev_point = point # don't update prev_point unless it has at least one prediction + prev_point = ( + point # don't update prev_point unless it has at least one prediction + ) else: # no predictions for this point, so ignore current point and previous point to attempt to recover sequence; # if gap between current point and prev_point is too big, abandon the prev_point and reset; @@ -346,9 +517,16 @@ def get_trace_matches(source_feature: MatchableFeature, target_candidates: Itera if prev_point.index > 0: prev_point = points[prev_point.index - 1] # gap with no candidates too big if 60seconds or 200m since last point - if (time_since_prev_point is not None and time_since_prev_point > options.broken_time_gap_reset_sequence) or \ - trace_dist_from_prev_point > options.broken_distance_gap_reset_sequence: - #print(rf"#{str(idx)}: sequence break; time_since_prev_point={time_since_prev_point} trace_dist_from_prev_point={trace_dist_from_prev_point}") + if ( + ( + time_since_prev_point is not None + and time_since_prev_point + > options.broken_time_gap_reset_sequence + ) + or trace_dist_from_prev_point + > options.broken_distance_gap_reset_sequence + ): + # print(rf"#{str(idx)}: sequence break; time_since_prev_point={time_since_prev_point} trace_dist_from_prev_point={trace_dist_from_prev_point}") # we have a sequence break, reset prev point, new sequence will start from next point sequence_breaks += 1 prev_point = None @@ -360,10 +538,19 @@ def get_trace_matches(source_feature: MatchableFeature, target_candidates: Itera end = timer() elapsed = end - start source_feature_length = get_linestring_length(source_feature.geometry) - t = TraceMatchResult(source_feature.id, source_feature.geometry.wkt, points, source_feature_length, len(target_candidates), elapsed=elapsed, sequence_breaks=sequence_breaks) + t = TraceMatchResult( + source_feature.id, + source_feature.geometry.wkt, + points, + source_feature_length, + len(target_candidates), + elapsed=elapsed, + sequence_breaks=sequence_breaks, + ) set_trace_match_metrics(t) return t + def set_trace_match_metrics(t: TraceMatchResult) -> None: matched_target_ids = set() route_length = 0 @@ -372,56 +559,108 @@ def set_trace_match_metrics(t: TraceMatchResult) -> None: revisited_segments = 0 points_with_matches = 0 for point in t.points: - if point.best_prediction is not None and point.best_prediction.referenced_feature is not None: + if ( + point.best_prediction is not None + and point.best_prediction.referenced_feature is not None + ): points_with_matches += 1 - route_length += point.best_prediction.route_distance_to_prev_point if point.best_prediction.route_distance_to_prev_point is not None else 0 + route_length += ( + point.best_prediction.route_distance_to_prev_point + if point.best_prediction.route_distance_to_prev_point is not None + else 0 + ) dist_to_road += point.best_prediction.distance_to_snapped_road - revisited_via_points += point.best_prediction.best_revisited_via_points_count + revisited_via_points += ( + point.best_prediction.best_revisited_via_points_count + ) revisited_segments += point.best_prediction.best_revisited_segments_count matched_target_ids.add(point.best_prediction.referenced_feature.id) t.matched_target_ids = list(matched_target_ids) t.points_with_matches = points_with_matches t.route_length = round(route_length, 2) - t.avg_dist_to_road = round(dist_to_road / points_with_matches, 2) if points_with_matches > 0 else None + t.avg_dist_to_road = ( + round(dist_to_road / points_with_matches, 2) + if points_with_matches > 0 + else None + ) t.revisited_via_points = revisited_via_points t.revisited_segments = revisited_segments -def print_stats(source_features: Iterable[MatchableFeature], target_features: Iterable[MatchableFeature], match_results: Iterable[TraceMatchResult], total_elapsed: float, avg_runtime_per_feature: float): + +def print_stats( + source_features: Iterable[MatchableFeature], + target_features: Iterable[MatchableFeature], + match_results: Iterable[TraceMatchResult], + total_elapsed: float, + avg_runtime_per_feature: float, +): num_traces = len(source_features) - total_route_length = sum([r.route_length for r in match_results]) / 1000 # in km - total_traces_length = sum([r.source_length for r in match_results]) / 1000 # in km + total_route_length = sum([r.route_length for r in match_results]) / 1000 # in km + total_traces_length = sum([r.source_length for r in match_results]) / 1000 # in km total_candidates = sum([r.target_candidates_count for r in match_results]) total_matches = sum([len(r.matched_target_ids) for r in match_results]) total_sequence_breaks = sum([r.sequence_breaks for r in match_results]) total_revisited_via_points = sum([r.revisited_via_points for r in match_results]) total_revisited_segments = sum([r.revisited_segments for r in match_results]) - total_traces_with_matches = sum([1 for r in match_results if r.points_with_matches > 0]) - total_avg_dist_to_road = sum([r.avg_dist_to_road for r in match_results if r.points_with_matches > 0]) - avg_runtime_per_km = total_elapsed / total_traces_length if total_traces_length > 0 else None - avg_dist_to_road = round(total_avg_dist_to_road / total_traces_with_matches, 2) if total_traces_with_matches > 0 else None + total_traces_with_matches = sum( + [1 for r in match_results if r.points_with_matches > 0] + ) + total_avg_dist_to_road = sum( + [r.avg_dist_to_road for r in match_results if r.points_with_matches > 0] + ) + avg_runtime_per_km = ( + total_elapsed / total_traces_length if total_traces_length > 0 else None + ) + avg_dist_to_road = ( + round(total_avg_dist_to_road / total_traces_with_matches, 2) + if total_traces_with_matches > 0 + else None + ) print("==================================================================") print("Totals:") print("==================================================================") print(rf"Traces.............................{num_traces}") print(rf"Target features....................{len(target_features)}") - print(rf"Elapsed:...........................{round(total_elapsed//60)}min {total_elapsed%60:.3f}s") + print( + rf"Elapsed:...........................{round(total_elapsed // 60)}min {total_elapsed % 60:.3f}s" + ) print(rf"Avg runtime/trace..................{avg_runtime_per_feature:.3f}s") print(rf"Avg runtime/km.....................{avg_runtime_per_km:.3f}s") print(rf"Avg distance to snapped road.......{avg_dist_to_road}m") print(rf"Snapped route length...............{total_route_length:.2f}km") print(rf"GPS traces length..................{total_traces_length:.2f}km") - print(rf"Snapped route len/gps len..........{(total_route_length/total_traces_length):.2f}") - print(rf"Avg number of candidate segments...{(total_candidates/num_traces):.2f}/trace, {(total_candidates/total_traces_length):.2f}/km") - print(rf"Avg number of matched segments.....{(total_matches/num_traces):.2f}/trace, {(total_matches/total_traces_length):.2f}/km") - print(rf"Avg number of sequence breaks......{(total_sequence_breaks/num_traces):.2f}/trace, {(total_sequence_breaks/total_traces_length):.2f}/km") - print(rf"Avg number of revisited via points.{(total_revisited_via_points/num_traces):.2f}/trace, {(total_revisited_via_points/total_traces_length):.2f}/km") - print(rf"Avg number of revisited segments...{(total_revisited_segments/num_traces):.2f}/trace, {(total_revisited_segments/total_traces_length):.2f}/km") + print( + rf"Snapped route len/gps len..........{(total_route_length / total_traces_length):.2f}" + ) + print( + rf"Avg number of candidate segments...{(total_candidates / num_traces):.2f}/trace, {(total_candidates / total_traces_length):.2f}/km" + ) + print( + rf"Avg number of matched segments.....{(total_matches / num_traces):.2f}/trace, {(total_matches / total_traces_length):.2f}/km" + ) + print( + rf"Avg number of sequence breaks......{(total_sequence_breaks / num_traces):.2f}/trace, {(total_sequence_breaks / total_traces_length):.2f}/km" + ) + print( + rf"Avg number of revisited via points.{(total_revisited_via_points / num_traces):.2f}/trace, {(total_revisited_via_points / total_traces_length):.2f}/km" + ) + print( + rf"Avg number of revisited segments...{(total_revisited_segments / num_traces):.2f}/trace, {(total_revisited_segments / total_traces_length):.2f}/km" + ) print("==================================================================") -def snap_traces(features_to_match_file: str, overture_file: str, output_file: str, res: int, snap_options: TraceSnapOptions=None, output_for_judgment: bool=False) -> None: + +def snap_traces( + features_to_match_file: str, + overture_file: str, + output_file: str, + res: int, + snap_options: TraceSnapOptions = None, + output_for_judgment: bool = False, +) -> None: if snap_options is None: - snap_options = TraceSnapOptions() # loads default options + snap_options = TraceSnapOptions() # loads default options # save the options we used next to the output file for debugging or comparison with other runs with open(output_file + ".options.json", "w") as f: @@ -430,19 +669,21 @@ def snap_traces(features_to_match_file: str, overture_file: str, output_file: st start = timer() print("Loading features...") to_match_prop_filter = {} - #to_match_prop_filter["id"] = "manual_trace#4" + # to_match_prop_filter["id"] = "manual_trace#4" to_match = load_matchable_set(features_to_match_file, is_multiline=False, res=res) features_to_match = to_match.features_by_id.values() if len(features_to_match) == 0: print("no features to match") exit() - overture = load_matchable_set(overture_file, is_multiline=True, properties_filter = {"type": "segment"}, res=res) - features_overture =overture.features_by_id.values() + overture = load_matchable_set( + overture_file, is_multiline=True, properties_filter={"type": "segment"}, res=res + ) + features_overture = overture.features_by_id.values() print("Features to match: " + str(len(features_to_match))) print("Features Overture: " + str(len(features_overture))) end = timer() - print(f"Loading time: {(end-start):.2f}s") + print(f"Loading time: {(end - start):.2f}s") i = 0 match_results = [] @@ -450,46 +691,142 @@ def snap_traces(features_to_match_file: str, overture_file: str, output_file: st for source_feature in features_to_match: i += 1 - target_candidates = get_features_with_cells(overture.features_by_cell, to_match.cells_by_id[source_feature.id]) + target_candidates = get_features_with_cells( + overture.features_by_cell, to_match.cells_by_id[source_feature.id] + ) match_res = get_trace_matches(source_feature, target_candidates, snap_options) match_results.append(match_res) total_elapsed += match_res.elapsed avg_runtime_per_feature = total_elapsed / i - if i%1 == 0: - print(rf"trace#{str(i)} length={match_res.source_length} route_length={round(match_res.route_length)} " + \ - rf"points={len(source_feature.geometry.coords)} points_w_matches={match_res.points_with_matches} " + \ - rf"candidates={match_res.target_candidates_count} matched target_ids: {str(len(match_res.matched_target_ids))} " + \ - rf"elapsed: {match_res.elapsed:.2f}s; avg runtime/feature: {avg_runtime_per_feature:.3f}s") - - print_stats(features_to_match, features_overture, match_results, total_elapsed, avg_runtime_per_feature) + if i % 1 == 0: + print( + rf"trace#{str(i)} length={match_res.source_length} route_length={round(match_res.route_length)} " + + rf"points={len(source_feature.geometry.coords)} points_w_matches={match_res.points_with_matches} " + + rf"candidates={match_res.target_candidates_count} matched target_ids: {str(len(match_res.matched_target_ids))} " + + rf"elapsed: {match_res.elapsed:.2f}s; avg runtime/feature: {avg_runtime_per_feature:.3f}s" + ) + + print_stats( + features_to_match, + features_overture, + match_results, + total_elapsed, + avg_runtime_per_feature, + ) print("Writing results...") start = timer() output_trace_snap_results(match_results, output_file, output_for_judgment) end = timer() - print(f"Writing time: {(end-start):.2f}s") - calculate_error_rate(features_to_match_file.replace('.geojson', '.labeled.txt'), overture.features_by_id, match_results) + print(f"Writing time: {(end - start):.2f}s") + calculate_error_rate( + features_to_match_file.replace(".geojson", ".labeled.txt"), + overture.features_by_id, + match_results, + ) + def get_args(): - parser = argparse.ArgumentParser(description="", add_help=True, formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("--input-to-match", help="Input file containing features to match in geojson format", required=True) - parser.add_argument("--input-overture", help="Input file containing overture features", required=True) - parser.add_argument("--output", help="Output file containing match results", required=True) - parser.add_argument("--resolution", help="H3 cell resolution used to pre-filter candidates", type=int, default=constants.DEFAULT_H3_RESOLUTION, choices=range(0,15)) - parser.add_argument("--sigma", type=float, help=f"Sigma param - controlling tolerance to GPS noise", required=False, default=constants.DEFAULT_SIGMA) - parser.add_argument("--beta", type=float, help=f"Beta param - controlling confidence in route", required=False, default=constants.DEFAULT_BETA) - parser.add_argument("--allow_loops", type=bool, help=f"Allow same sequence to revisit same segment with other segment(s) in between", required=False, default=constants.DEFAULT_ALLOW_LOOPS) - parser.add_argument("--max_point_to_road_distance", type=float, help=f"Maximum distance in meters between a trace point and a match candidate road", required=False, default=constants.DEFAULT_MAX_POINT_TO_ROAD_DISTANCE) - parser.add_argument("--max_route_to_trace_distance_difference", type=float, help=f"Maximum difference between route and trace lengths in meters", required=False, default=constants.DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE) - parser.add_argument("--revisit_segment_penalty_weight", type=float, help="How much to penalize a route with one segment revisit", required=False, default=constants.DEFAULT_SEGMENT_REVISIT_PENALTY) - parser.add_argument("--revisit_via_point_penalty_weight", type=float, help="How much to penalize a route with one via-point revisit", required=False, default=constants.DEFAULT_VIA_POINT_PENALTY_WEIGHT) - parser.add_argument("--broken_time_gap_reset_sequence", type=float, help="How big the time gap in seconds between points without valid route options before we consider it a broken sequence", required=False, default=constants.DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE) - parser.add_argument("--broken_distance_gap_reset_sequence", type=float, help="How big the distance gap in meters between points without valid route options before we consider it a broken sequence", required=False, default=constants.DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE) - parser.add_argument("--j", action="store_true", help="Also output the matches as a 'pre-labeled' file for judgment", default=False, required=False) + parser = argparse.ArgumentParser( + description="", + add_help=True, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "--input-to-match", + help="Input file containing features to match in geojson format", + required=True, + ) + parser.add_argument( + "--input-overture", + help="Input file containing overture features", + required=True, + ) + parser.add_argument( + "--output", help="Output file containing match results", required=True + ) + parser.add_argument( + "--resolution", + help="H3 cell resolution used to pre-filter candidates", + type=int, + default=constants.DEFAULT_H3_RESOLUTION, + choices=range(0, 15), + ) + parser.add_argument( + "--sigma", + type=float, + help="Sigma param - controlling tolerance to GPS noise", + required=False, + default=constants.DEFAULT_SIGMA, + ) + parser.add_argument( + "--beta", + type=float, + help="Beta param - controlling confidence in route", + required=False, + default=constants.DEFAULT_BETA, + ) + parser.add_argument( + "--allow_loops", + type=bool, + help="Allow same sequence to revisit same segment with other segment(s) in between", + required=False, + default=constants.DEFAULT_ALLOW_LOOPS, + ) + parser.add_argument( + "--max_point_to_road_distance", + type=float, + help="Maximum distance in meters between a trace point and a match candidate road", + required=False, + default=constants.DEFAULT_MAX_POINT_TO_ROAD_DISTANCE, + ) + parser.add_argument( + "--max_route_to_trace_distance_difference", + type=float, + help="Maximum difference between route and trace lengths in meters", + required=False, + default=constants.DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE, + ) + parser.add_argument( + "--revisit_segment_penalty_weight", + type=float, + help="How much to penalize a route with one segment revisit", + required=False, + default=constants.DEFAULT_SEGMENT_REVISIT_PENALTY, + ) + parser.add_argument( + "--revisit_via_point_penalty_weight", + type=float, + help="How much to penalize a route with one via-point revisit", + required=False, + default=constants.DEFAULT_VIA_POINT_PENALTY_WEIGHT, + ) + parser.add_argument( + "--broken_time_gap_reset_sequence", + type=float, + help="How big the time gap in seconds between points without valid route options before we consider it a broken sequence", + required=False, + default=constants.DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE, + ) + parser.add_argument( + "--broken_distance_gap_reset_sequence", + type=float, + help="How big the distance gap in meters between points without valid route options before we consider it a broken sequence", + required=False, + default=constants.DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE, + ) + parser.add_argument( + "--j", + action="store_true", + help="Also output the matches as a 'pre-labeled' file for judgment", + default=False, + required=False, + ) return parser.parse_args() + def get_trace_snap_options_from_args(args): return TraceSnapOptions( sigma=args.sigma, @@ -500,9 +837,18 @@ def get_trace_snap_options_from_args(args): revisit_segment_penalty_weight=args.revisit_segment_penalty_weight, revisit_via_point_penalty_weight=args.revisit_via_point_penalty_weight, broken_time_gap_reset_sequence=args.broken_time_gap_reset_sequence, - broken_distance_gap_reset_sequence=args.broken_distance_gap_reset_sequence) + broken_distance_gap_reset_sequence=args.broken_distance_gap_reset_sequence, + ) + if __name__ == "__main__": args = get_args() trace_snap_options = get_trace_snap_options_from_args(args) - snap_traces(args.input_to_match, args.input_overture, args.output, args.resolution, trace_snap_options, output_for_judgment=args.j) + snap_traces( + args.input_to_match, + args.input_overture, + args.output, + args.resolution, + trace_snap_options, + output_for_judgment=args.j, + ) diff --git a/gers/examples/python/route_utils.py b/gers/examples/python/route_utils.py index 51cbe71bc..ea497b8a0 100644 --- a/gers/examples/python/route_utils.py +++ b/gers/examples/python/route_utils.py @@ -1,10 +1,20 @@ -from utils import get_distance -from shapely.ops import nearest_points -from match_classes import RouteStep, Route, MatchableFeature +from collections.abc import Iterable + +from match_classes import MatchableFeature, Route, RouteStep from shapely.geometry import Point -from typing import Dict, Tuple, Iterable +from shapely.ops import nearest_points +from utils import get_distance -def get_route_step_dist(feat_before_from: MatchableFeature, feat_from: MatchableFeature, feat_to: MatchableFeature, start_feature: MatchableFeature, end_feature: MatchableFeature, start_point: Point, end_point: Point) -> Tuple[Point, float]: + +def get_route_step_dist( + feat_before_from: MatchableFeature, + feat_from: MatchableFeature, + feat_to: MatchableFeature, + start_feature: MatchableFeature, + end_feature: MatchableFeature, + start_point: Point, + end_point: Point, +) -> tuple[Point, float]: """get distance traveled on one feature `feat_from` having entering from `feat_before_from` and exiting to `feat_to`, given that the whole route starts at `start_feature` and ends at `end_feature`""" # todo: this a distance approximation for now as length of straight line from entry point to exit point on the feat_from feature, but works reasonably well for the data seen so far feat_from_exit_point, p2 = nearest_points(feat_from.geometry, feat_to.geometry) @@ -13,9 +23,11 @@ def get_route_step_dist(feat_before_from: MatchableFeature, feat_from: Matchable if feat_from.id == start_feature.id: d += get_distance(start_point, feat_from_exit_point) else: - p0_before, feat_from_entry_point = nearest_points(feat_before_from.geometry, feat_from.geometry) + p0_before, feat_from_entry_point = nearest_points( + feat_before_from.geometry, feat_from.geometry + ) d += get_distance(feat_from_entry_point, feat_from_exit_point) - + if feat_to.id == end_feature.id: d += get_distance(end_point, p2) # else there is no distance to add @@ -23,11 +35,21 @@ def get_route_step_dist(feat_before_from: MatchableFeature, feat_from: Matchable # todo: add basic penalties like allowed travel direction disagreement, road class change cost, etc. return feat_from_exit_point, d -def get_shortest_route(features: Iterable[MatchableFeature], feature_id_to_connected_features: Dict[str, Iterable[MatchableFeature]], start_feature: MatchableFeature, end_feature: MatchableFeature, start_point: Point, end_point: Point, allowed_ids: Iterable[str], blocked_ids: Iterable[str]) -> Route: + +def get_shortest_route( + features: Iterable[MatchableFeature], + feature_id_to_connected_features: dict[str, Iterable[MatchableFeature]], + start_feature: MatchableFeature, + end_feature: MatchableFeature, + start_point: Point, + end_point: Point, + allowed_ids: Iterable[str], + blocked_ids: Iterable[str], +) -> Route: """ Dijsktra's algorithm to find shortest route between start and end features. Remember for each traveled feature the entry via_point. """ - + # start and end are same feature, no route calculation needed, just distance if start_feature.id == end_feature.id: dist = get_distance(start_point, end_point) @@ -42,7 +64,7 @@ def get_shortest_route(features: Iterable[MatchableFeature], feature_id_to_conne for f in features: if f.id in blocked_ids and f.id != start_feature.id: continue - dist[f.id] = float('inf') + dist[f.id] = float("inf") prev[f.id] = None prev_via_point[f.id] = None feats_to_visit.append(f) @@ -51,41 +73,55 @@ def get_shortest_route(features: Iterable[MatchableFeature], feature_id_to_conne while len(feats_to_visit) > 0: current_feature = feats_to_visit[0] - min_dist = float('inf') + min_dist = float("inf") for f in feats_to_visit: if dist[f.id] < min_dist: min_dist = dist[f.id] current_feature = f - if min_dist == float('inf'): - break # no more allowed connected features to visit + if min_dist == float("inf"): + break # no more allowed connected features to visit if current_feature.id == end_feature.id: - break # done, visited end_feature, don't need to calculate shortest path to all features + break # done, visited end_feature, don't need to calculate shortest path to all features feats_to_visit.remove(current_feature) ids_to_visit.remove(current_feature.id) connected_features = feature_id_to_connected_features[current_feature.id] for v in connected_features: - if not(v.id in allowed_ids) or (v.id in blocked_ids) or not(v.id in ids_to_visit): + if ( + v.id not in allowed_ids + or (v.id in blocked_ids) + or v.id not in ids_to_visit + ): continue - if not(v.id in ids_to_visit): - continue # have already visited this feature + if v.id not in ids_to_visit: + continue # have already visited this feature - via_point, d = get_route_step_dist(prev[current_feature.id], current_feature, v, start_feature, end_feature, start_point, end_point) + via_point, d = get_route_step_dist( + prev[current_feature.id], + current_feature, + v, + start_feature, + end_feature, + start_point, + end_point, + ) alternate_dist = dist[current_feature.id] + d if alternate_dist < dist[v.id]: dist[v.id] = alternate_dist prev[v.id] = current_feature prev_via_point[v.id] = via_point - + steps = [] current_feature = end_feature if prev[current_feature.id] is not None or current_feature.id == start_feature.id: while current_feature is not None: - steps.insert(0, RouteStep(current_feature, prev_via_point[current_feature.id])) + steps.insert( + 0, RouteStep(current_feature, prev_via_point[current_feature.id]) + ) current_feature = prev[current_feature.id] r = Route(round(dist[end_feature.id], 2), steps) - return r \ No newline at end of file + return r diff --git a/gers/examples/python/tests/match_traces_test.py b/gers/examples/python/tests/match_traces_test.py index 07cd77a8b..90a1b6b3f 100644 --- a/gers/examples/python/tests/match_traces_test.py +++ b/gers/examples/python/tests/match_traces_test.py @@ -1,20 +1,26 @@ -import test_setup -import os import json +import os import unittest + import constants from match_classes import TraceSnapOptions from match_traces import get_trace_matches -from utils import load_matchable_set, get_features_with_cells +from utils import get_features_with_cells, load_matchable_set -class TestTraces(unittest.TestCase): +class TestTraces(unittest.TestCase): def test_match_traces(self): - features_to_match_file = os.path.join(constants.DATA_DIR, "macon-manual-traces.geojson") - overture_file = os.path.join(constants.DATA_DIR, "overture-transportation-macon.geojson") + features_to_match_file = os.path.join( + constants.DATA_DIR, "macon-manual-traces.geojson" + ) + overture_file = os.path.join( + constants.DATA_DIR, "overture-transportation-macon.geojson" + ) res = 12 - to_match = load_matchable_set(features_to_match_file, is_multiline=False, res=res) + to_match = load_matchable_set( + features_to_match_file, is_multiline=False, res=res + ) self.assertIsNotNone(to_match) self.assertEqual(len(to_match.features_by_id), 4) @@ -22,19 +28,26 @@ def test_match_traces(self): self.assertIn(id_to_match, to_match.features_by_id) source_feature = to_match.features_by_id[id_to_match] - overture = load_matchable_set(overture_file, is_multiline=True, properties_filter = {"type": "segment"}, res=res) + overture = load_matchable_set( + overture_file, + is_multiline=True, + properties_filter={"type": "segment"}, + res=res, + ) self.assertIsNotNone(overture.features_by_id) self.assertGreater(len(overture.features_by_id), 20000) options = TraceSnapOptions(max_point_to_road_distance=30) - target_candidates = get_features_with_cells(overture.features_by_cell, to_match.cells_by_id[source_feature.id]) + target_candidates = get_features_with_cells( + overture.features_by_cell, to_match.cells_by_id[source_feature.id] + ) match_res = get_trace_matches(source_feature, target_candidates, options) self.assertIsNotNone(match_res) self.assertIsNotNone(match_res.points) self.assertEqual(len(match_res.points), len(source_feature.geometry.coords)) self.assertGreater(match_res.source_length, 5000) - self.assertGreater(match_res.route_length, 5000) + self.assertGreater(match_res.route_length, 5000) json_res = match_res.to_json() self.assertIsNotNone(json_res) @@ -48,5 +61,6 @@ def test_match_traces(self): if idx > 0: self.assertGreater(bp.route_distance_to_prev_point, 0.0) -if __name__ == '__main__': - unittest.main() \ No newline at end of file + +if __name__ == "__main__": + unittest.main() diff --git a/gers/examples/python/tests/test_setup.py b/gers/examples/python/tests/test_setup.py index 457368324..2bf5cc41a 100644 --- a/gers/examples/python/tests/test_setup.py +++ b/gers/examples/python/tests/test_setup.py @@ -1,5 +1,5 @@ -import sys import os +import sys parent_dir = os.path.dirname(os.path.abspath(__file__)) -sys.path.insert(0, os.path.dirname(parent_dir)) \ No newline at end of file +sys.path.insert(0, os.path.dirname(parent_dir)) diff --git a/gers/examples/python/tests/utils_test.py b/gers/examples/python/tests/utils_test.py index 22e82586b..6733e4999 100644 --- a/gers/examples/python/tests/utils_test.py +++ b/gers/examples/python/tests/utils_test.py @@ -1,14 +1,21 @@ -import test_setup import os import unittest + import constants -from utils import get_distance, get_linestring_length, get_intersecting_h3_cells_for_geo_json, load_matchable_set -from shapely import Point, LineString +from shapely import LineString, Point +from utils import ( + get_distance, + get_intersecting_h3_cells_for_geo_json, + get_linestring_length, + load_matchable_set, +) -class TestUtils(unittest.TestCase): +class TestUtils(unittest.TestCase): def test_load_matchable_set_geojson(self): - features_to_match_file = os.path.join(constants.DATA_DIR, "macon-manual-traces.geojson") + features_to_match_file = os.path.join( + constants.DATA_DIR, "macon-manual-traces.geojson" + ) s = load_matchable_set(features_to_match_file, res=12, is_multiline=False) self.assertIsNotNone(s) self.assertEqual(len(s.features_by_id), 4) @@ -18,35 +25,60 @@ def test_load_matchable_set_geojson(self): def test_get_distance(self): p1 = Point(-83.6878343, 32.8413587) p2 = Point(-83.6877941, 32.8413903) - + d = get_distance(p1, p2) self.assertAlmostEqual(d, 5.1, delta=0.1) def test_get_linestring_length(self): l = LineString([(-83.6878343, 32.8413587), (-83.6877941, 32.8413903)]) - + d = get_linestring_length(l) - self.assertAlmostEqual(d, 5.1, delta=0.1) + self.assertAlmostEqual(d, 5.1, delta=0.1) def test_get_intersecting_h3_cells_for_geo_json(self): - point = { "type": "Point", "coordinates": [-83.6197063, 32.8589311] } + point = {"type": "Point", "coordinates": [-83.6197063, 32.8589311]} actual_cells = get_intersecting_h3_cells_for_geo_json(point, 10) expected_cells = ["8a44c0a32867fff"] self.assertCountEqual(actual_cells, expected_cells) - line = { "type": "LineString", "coordinates": [[-83.61940200000001, 32.858034], [-83.61940200000001, 32.859538]] } + line = { + "type": "LineString", + "coordinates": [ + [-83.61940200000001, 32.858034], + [-83.61940200000001, 32.859538], + ], + } actual_cells = get_intersecting_h3_cells_for_geo_json(line, 10) expected_cells = ["8a44c0a3295ffff", "8a44c0a32867fff"] self.assertCountEqual(actual_cells, expected_cells) - polygon = { "type": "Polygon", "coordinates": [[[-83.6195695, 32.8591587], [-83.6192584, 32.8583723], [-83.619135, 32.8590437], [-83.6195695, 32.8591587]]] } + polygon = { + "type": "Polygon", + "coordinates": [ + [ + [-83.6195695, 32.8591587], + [-83.6192584, 32.8583723], + [-83.619135, 32.8590437], + [-83.6195695, 32.8591587], + ] + ], + } actual_cells = get_intersecting_h3_cells_for_geo_json(polygon, 10) expected_cells = ["8a44c0a32877fff", "8a44c0a32867fff", "8a44c0a3295ffff"] self.assertCountEqual(actual_cells, expected_cells) - ml = { "type": "MultiLineString", "coordinates": [[[-83.61940200000001, 32.858034], [-83.61940200000001, 32.859538]], [[-83.6215878, 32.8580366], [-83.6202145, 32.8580546]]] } + ml = { + "type": "MultiLineString", + "coordinates": [ + [[-83.61940200000001, 32.858034], [-83.61940200000001, 32.859538]], + [[-83.6215878, 32.8580366], [-83.6202145, 32.8580546]], + ], + } actual_cells = get_intersecting_h3_cells_for_geo_json(ml, 10) - expected_cells = ["8a44c0a3294ffff", "8a44c0a32867fff", "8a44c0a304b7fff", "8a44c0a3295ffff"] + expected_cells = [ + "8a44c0a3294ffff", + "8a44c0a32867fff", + "8a44c0a304b7fff", + "8a44c0a3295ffff", + ] self.assertCountEqual(actual_cells, expected_cells) - - diff --git a/gers/examples/python/utils.py b/gers/examples/python/utils.py index c66599f24..7d613b19a 100644 --- a/gers/examples/python/utils.py +++ b/gers/examples/python/utils.py @@ -1,116 +1,142 @@ import csv import json -from haversine import haversine, Unit -#from shapely.ops import transform -from shapely import wkt -from shapely.geometry import shape, mapping -from shapely.geometry.base import BaseGeometry +from collections.abc import Iterable +from typing import Any + from dateutil import parser -from typing import Any, Dict, Iterable from h3 import h3 - +from haversine import Unit, haversine from match_classes import MatchableFeature, MatchableFeaturesSet -#from pyproj import Geod + +# from shapely.ops import transform +from shapely import wkt +from shapely.geometry import mapping, shape +from shapely.geometry.base import BaseGeometry + +# from pyproj import Geod + def get_seconds_elapsed(t1_str, t2_str): t1 = parser.parse(t1_str) t2 = parser.parse(t2_str) return (t2 - t1).total_seconds() + def get_linestring_length(ls): length = 0 for i in range(len(ls.coords) - 1): lon1, lat1 = ls.coords[i] - lon2, lat2 = ls.coords[i+1] - #_, _, d = geod.inv(lon1, lat1, lon2, lat2) + lon2, lat2 = ls.coords[i + 1] + # _, _, d = geod.inv(lon1, lat1, lon2, lat2) d = haversine((lat1, lon1), (lat2, lon2), unit=Unit.METERS) - length += d + length += d return round(length, 2) + def get_distance(point1, point2): - #_, _, d = geod.inv(point1.x, point1.y, point2.x, point2.y) + # _, _, d = geod.inv(point1.x, point1.y, point2.x, point2.y) d = haversine((point1.y, point1.x), (point2.y, point2.x), unit=Unit.METERS) return round(d, 2) + def get_intersecting_h3_cells_for_line(coords, res): """for coordinates of a linestring, gets all h3 cells of given resolution that intersect the line""" - cells = set() - prevCell = None + cells = set() + prevCell = None for coord in coords: cell = h3.geo_to_h3(coord[1], coord[0], res) cells.add(cell) - if (prevCell is None): + if prevCell is None: prevCell = cell else: - if (prevCell != cell): + if prevCell != cell: # two consecutive coordinates in the linestring may be more than one cell apart # need to find intermediate cells between previous cell and the current one - if (not h3.h3_indexes_are_neighbors(prevCell, cell)): + if not h3.h3_indexes_are_neighbors(prevCell, cell): intermediateCells = h3.h3_line(prevCell, cell) for intermediateCell in intermediateCells: cells.add(intermediateCell) prevCell = cell return cells -def get_intersecting_h3_cells_for_geo_json(geometry: Any, res:int) -> Iterable[str]: + +def get_intersecting_h3_cells_for_geo_json(geometry: Any, res: int) -> Iterable[str]: """gets all h3 cells of given resolution that intersect the geometry.""" # h3 api wants two floats for point, geojson dict for polygon and custom code is needed for line and multi* geometries geojson = mapping(geometry) if isinstance(geometry, BaseGeometry) else geometry geom_type = geojson["type"] coords = geojson["coordinates"] - if (geom_type.startswith("Multi")): + if geom_type.startswith("Multi"): sub_geom_type = geom_type.replace("Multi", "") - sub_geoms = [{"type": sub_geom_type, "coordinates": sub_geom_coords } for sub_geom_coords in coords] - sub_cells = [sub_cell for sub_geom in sub_geoms for sub_cell in get_intersecting_h3_cells_for_geo_json(sub_geom, res)] + sub_geoms = [ + {"type": sub_geom_type, "coordinates": sub_geom_coords} + for sub_geom_coords in coords + ] + sub_cells = [ + sub_cell + for sub_geom in sub_geoms + for sub_cell in get_intersecting_h3_cells_for_geo_json(sub_geom, res) + ] return set(sub_cells) - if (geom_type == "Point"): + if geom_type == "Point": return set([h3.geo_to_h3(coords[1], coords[0], res)]) - if (geom_type == "LineString"): + if geom_type == "LineString": return get_intersecting_h3_cells_for_line(coords, res) - if (geom_type == "Polygon"): - innerCells = h3.polyfill(geojson, res, True) # this only covers the tiles whose centers are inside the polygon + if geom_type == "Polygon": + innerCells = h3.polyfill( + geojson, res, True + ) # this only covers the tiles whose centers are inside the polygon boundaryCells = get_intersecting_h3_cells_for_line(coords[0], res) return innerCells | boundaryCells -def matches_properties_filter(feature: Dict[str, Any], properties_filter: Dict[str, Any]) -> bool: + +def matches_properties_filter( + feature: dict[str, Any], properties_filter: dict[str, Any] +) -> bool: if properties_filter is None: return True feat_props = feature.get("properties") for prop in properties_filter: if prop == "id": return feature.get("id") == properties_filter[prop] - - if not prop in feat_props or (properties_filter[prop] != "*" and feat_props[prop] != properties_filter[prop]): + + if prop not in feat_props or ( + properties_filter[prop] != "*" + and feat_props[prop] != properties_filter[prop] + ): return False - return True + return True + -def get_matchable_feature(feature_dict: Dict[str, Any]) -> MatchableFeature: +def get_matchable_feature(feature_dict: dict[str, Any]) -> MatchableFeature: """creates a MatchableFeature from a dict with expected keys [id, geometry, properties], which could be either a geojson or parsed from a csv file with wkt geometry""" id = feature_dict.get("id") - geom = feature_dict.get("geometry") + geom = feature_dict.get("geometry") if type(geom) is dict and "type" in geom and "coordinates" in geom: # if it"s a geojson feature - s = shape(geom) - elif isinstance(geom, str): + s = shape(geom) + elif isinstance(geom, str): # if it"s a wkt string s = wkt.loads(geom) - props = feature_dict.get("properties") + props = feature_dict.get("properties") return MatchableFeature(id, s, props) -def get_feature_cells(geom: Any, res: int, k_rings_to_add:int=1): + +def get_feature_cells(geom: Any, res: int, k_rings_to_add: int = 1): """gets all h3 cells of given resolution that intersect the geometry, and also the cells that are k rings around the intersecting cells""" h3_cells = get_intersecting_h3_cells_for_geo_json(geom, res) if k_rings_to_add == 0: return list(h3_cells) - + rings = [h3.k_ring(h, k_rings_to_add) for h in h3_cells] return list(set(cell for r in rings for cell in r)) -def parse_geojson(filename: str, is_multiline: bool) -> Iterable[Dict[str, Any]]: - with open(filename, mode="r", errors="ignore") as file: + +def parse_geojson(filename: str, is_multiline: bool) -> Iterable[dict[str, Any]]: + with open(filename, errors="ignore") as file: if is_multiline: # text file with one geojson per line - i=0 + i = 0 features = [] for line in file: i += 1 @@ -118,16 +144,22 @@ def parse_geojson(filename: str, is_multiline: bool) -> Iterable[Dict[str, Any]] geojson = json.loads(line.strip().rstrip(",")) features.append(geojson) except Exception as x: - print(fr"Line {i}: " + str(x)) + print(rf"Line {i}: " + str(x)) return features else: full_gj = json.loads(file.read()) - if full_gj.get("type") == "FeatureCollection": + if full_gj.get("type") == "FeatureCollection": return full_gj.get("features") else: return [full_gj] -def get_matchable_set(features: Iterable[Dict[str, Any]], properties_filter: dict=None, res: int=12, limit_feature_count=-1) -> MatchableFeaturesSet: + +def get_matchable_set( + features: Iterable[dict[str, Any]], + properties_filter: dict = None, + res: int = 12, + limit_feature_count=-1, +) -> MatchableFeaturesSet: features_by_id = {} cells_by_id = {} features_by_cell = {} @@ -140,7 +172,7 @@ def get_matchable_set(features: Iterable[Dict[str, Any]], properties_filter: dic features_by_id[feature.id] = feature cells_by_id[feature.id] = get_feature_cells(feature.geometry, res) for cell in cells_by_id[feature.id]: - if not cell in features_by_cell: + if cell not in features_by_cell: features_by_cell[cell] = [] features_by_cell[cell].append(feature) except Exception as x: @@ -150,11 +182,12 @@ def get_matchable_set(features: Iterable[Dict[str, Any]], properties_filter: dic break return MatchableFeaturesSet(features_by_id, cells_by_id, features_by_cell) -def parse_csv(filename: str, delimiter: str=",") -> MatchableFeaturesSet: + +def parse_csv(filename: str, delimiter: str = ",") -> MatchableFeaturesSet: features = [] - i=0 - with open(filename, mode="r", errors="ignore") as file: - reader = csv.DictReader(file, delimiter=delimiter) + i = 0 + with open(filename, errors="ignore") as file: + reader = csv.DictReader(file, delimiter=delimiter) for row in reader: feat_dict = {} feat_dict["properties"] = {} @@ -162,28 +195,43 @@ def parse_csv(filename: str, delimiter: str=",") -> MatchableFeaturesSet: default_id = str(i) i += 1 key = k.lower() - if not "id" in feat_dict and "id" in key: + if "id" not in feat_dict and "id" in key: feat_dict["id"] = v - elif not "geometry" in feat_dict and ("geometry" in key or "wkt" in key): + elif "geometry" not in feat_dict and ( + "geometry" in key or "wkt" in key + ): feat_dict["geometry"] = v else: feat_dict["properties"][k] = v - if not "id" in feat_dict: + if "id" not in feat_dict: feat_dict["id"] = default_id - if not "geometry" in feat_dict: - if "lat" in feat_dict["properties"] and "lon" in feat_dict["properties"]: - feat_dict["geometry"] = f'POINT({feat_dict["properties"]["lon"]} {feat_dict["properties"]["lat"]})' + if "geometry" not in feat_dict: + if ( + "lat" in feat_dict["properties"] + and "lon" in feat_dict["properties"] + ): + feat_dict["geometry"] = ( + f"POINT({feat_dict['properties']['lon']} {feat_dict['properties']['lat']})" + ) else: continue - + features.append(feat_dict) return features -def load_matchable_set(filename: str, properties_filter: dict=None, res: int=12, limit_feature_count=-1, is_multiline: bool=False, delimiter: str=",") -> MatchableFeaturesSet: + +def load_matchable_set( + filename: str, + properties_filter: dict = None, + res: int = 12, + limit_feature_count=-1, + is_multiline: bool = False, + delimiter: str = ",", +) -> MatchableFeaturesSet: """loads a MatchableFeaturesSet from a geojson or csv file""" - extension = filename.split(".")[-1] + extension = filename.split(".")[-1] match extension: case "geojson" | "json": features = parse_geojson(filename, is_multiline=is_multiline) @@ -191,22 +239,26 @@ def load_matchable_set(filename: str, properties_filter: dict=None, res: int=12, features = parse_csv(filename, delimiter=delimiter) case _: raise Exception(f"Unsupported file type: {extension}") - + s = get_matchable_set(features, properties_filter, res, limit_feature_count) return s - -def get_features_with_cells(features_by_cell: Dict[str, Iterable[MatchableFeature]], cells_filter: Iterable[str]) -> Iterable[MatchableFeature]: + + +def get_features_with_cells( + features_by_cell: dict[str, Iterable[MatchableFeature]], cells_filter: Iterable[str] +) -> Iterable[MatchableFeature]: """gets all features in `features_by_cell` that intersect any of the cells in `cells_filter`""" with_cells = [] candidate_ids = set() for cell in cells_filter: if cell in features_by_cell: for candidate in features_by_cell[cell]: - if not candidate.id in candidate_ids: + if candidate.id not in candidate_ids: candidate_ids.add(candidate.id) with_cells.append(candidate) return with_cells + def write_json(results_json: Any, output_file_name: str): with open(output_file_name, "w") as f: - json.dump(results_json, f, indent=4) \ No newline at end of file + json.dump(results_json, f, indent=4) diff --git a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py index 9d9286941..209a06ae4 100644 --- a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py +++ b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py @@ -8,12 +8,12 @@ Feature, StrictBaseModel, ) -from overture.schema.core.geometry import ( +from overture.schema.core.types import CountryCode, TrimmedString +from overture.schema.foundation.primitive.geometry import ( Geometry, GeometryType, GeometryTypeConstraint, ) -from overture.schema.core.types import CountryCode, TrimmedString class AddressLevel(StrictBaseModel): diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py index b01ab7ab6..4e4eeffda 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py @@ -8,8 +8,12 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint -from overture.schema.core.models import CartographicallyHinted +from overture.schema.core.models import CartographicallyHinted, Stacked +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) class Bathymetry( diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py index 14dceaea6..ff87fe920 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py @@ -13,8 +13,12 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import Named, Stacked +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..enums import SurfaceMaterial diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py index 507d94e31..27b3fad4a 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py @@ -10,8 +10,12 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import Named, Stacked +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..enums import SurfaceMaterial diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py index 3c65cba97..29a4059b3 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py @@ -8,8 +8,12 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint -from overture.schema.core.models import CartographicallyHinted +from overture.schema.core.models import CartographicallyHinted, Stacked +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) class LandCover( diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py index 6a9f23229..228750b91 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py @@ -10,8 +10,12 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import Named, Stacked +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..enums import SurfaceMaterial diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py index 33d6e880b..ddedbf6d5 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py @@ -9,8 +9,12 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import Named, Stacked +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) class Water( diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py index 529df573b..3cfd45c40 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py @@ -5,8 +5,12 @@ from pydantic import ConfigDict, Field from overture.schema.core import Feature -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import Named, Stacked +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..models import Shape from .enums import ( diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py index 6faff6b4b..ba75e6981 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py @@ -5,10 +5,14 @@ from pydantic import Field from overture.schema.core import Feature -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import Named, Stacked from overture.schema.core.ref import Reference, Relationship from overture.schema.core.types import Id +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..building.models import Building from ..models import Shape diff --git a/packages/overture-schema-core/src/overture/schema/core/models.py b/packages/overture-schema-core/src/overture/schema/core/models.py index 97fe7f660..e67cb194a 100644 --- a/packages/overture-schema-core/src/overture/schema/core/models.py +++ b/packages/overture-schema-core/src/overture/schema/core/models.py @@ -13,12 +13,12 @@ from pydantic_core import core_schema from overture.schema.core.bbox import BBox +from overture.schema.foundation.primitive.geometry import Geometry from overture.schema.validation import ( allow_extension_fields, ) from .enums import NameVariant, PerspectiveMode, Side -from .geometry import Geometry from .types import ( CommonNames, ConfidenceScore, diff --git a/packages/overture-schema-core/tests/test_models.py b/packages/overture-schema-core/tests/test_models.py index 2c75ff5e5..0dcc0f9d2 100644 --- a/packages/overture-schema-core/tests/test_models.py +++ b/packages/overture-schema-core/tests/test_models.py @@ -4,9 +4,8 @@ import pytest from deepdiff import DeepDiff from overture.schema.core.bbox import BBox -from overture.schema.core.geometry import Geometry -from overture.schema.core.json_schema import EnhancedJsonSchemaGenerator from overture.schema.core.models import Feature +from overture.schema.foundation.primitive.geometry import Geometry from shapely.geometry import LineString, Point @@ -40,6 +39,7 @@ def test_feature_json_schema() -> None: "additionalProperties": False, "properties": { "between": { +<<<<<<< HEAD "items": { "maximum": 1.0, "minimum": 0.0, @@ -61,6 +61,47 @@ def test_feature_json_schema() -> None: "type": "string", }, "confidence": {"maximum": 1.0, "minimum": 0.0, "type": "number"}, +======= + "anyOf": [ + { + "items": { + "maximum": 1.0, + "minimum": 0.0, + "type": "number", + }, + "maxItems": 2, + "minItems": 2, + "type": "array", + }, + {"type": "null"}, + ], + "default": None, + }, + "property": {"type": "string"}, + "dataset": {"type": "string"}, + "record_id": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "default": None, + }, + "update_time": { + "anyOf": [ + { + "format": "date-time", + "pattern": "^([1-9]\\d{3})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])T([01]\\d|2[0-3]):([0-5]\\d):([0-5]\\d|60)(\\.\\d{1,3})?(Z|[-+]([01]\\d|2[0-3]):[0-5]\\d)$", + "type": "string", + }, + {"type": "null"}, + ], + "default": None, + }, + "confidence": { + "anyOf": [ + {"maximum": 1.0, "minimum": 0.0, "type": "number"}, + {"type": "null"}, + ], + "default": None, + }, +>>>>>>> cdf76fd (wip - repackage/modularize) }, "required": ["property", "dataset"], "type": "object", @@ -397,22 +438,50 @@ def test_feature_json_schema() -> None: ], }, "bbox": { +<<<<<<< HEAD "items": {"type": "number"}, "maxItems": 4, "minItems": 4, "type": "array", +======= + "anyOf": [ + { + "items": {"type": "number"}, + "maxItems": 4, + "minItems": 4, + "type": "array", + }, + {"type": "null"}, + ], + "default": None, +>>>>>>> cdf76fd (wip - repackage/modularize) }, "properties": { "type": "object", "properties": { "theme": {"type": "string"}, "type": {"type": "string"}, +<<<<<<< HEAD "version": {"maximum": 2147483647, "minimum": 0, "type": "integer"}, "sources": { "items": {"$ref": "#/$defs/SourcePropertyItem"}, "minItems": 1, "type": "array", "uniqueItems": True, +======= + "version": {"minimum": 0, "type": "integer"}, + "sources": { + "anyOf": [ + { + "items": {"$ref": "#/$defs/SourcePropertyItem"}, + "minItems": 1, + "type": "array", + "uniqueItems": True, + }, + {"type": "null"}, + ], + "default": None, +>>>>>>> cdf76fd (wip - repackage/modularize) }, }, "unevaluatedProperties": False, diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py index 2a366de1b..13b7907f2 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py @@ -8,7 +8,6 @@ Feature, ) from overture.schema.core.enums import Side -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import ( CartographicallyHinted, Named, @@ -27,6 +26,11 @@ from overture.schema.core.validation import ( UniqueItemsConstraint, ) +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..enums import DivisionClass, PlaceType from ..models import CapitalOfDivisionItem diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py index fd8ab3eba..9d7523a15 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py @@ -7,7 +7,6 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import ( Named, Names, @@ -17,6 +16,11 @@ from overture.schema.core.validation import ( exactly_one_of, ) +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..division.models import Division from ..enums import PlaceType diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py index c72eac518..c2eb9b2e8 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py @@ -7,7 +7,6 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import Perspectives from overture.schema.core.ref import Reference, Relationship from overture.schema.core.types import ( @@ -20,6 +19,11 @@ exactly_one_of, not_required_if, ) +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..division import Division from ..enums import PlaceType diff --git a/packages/overture-schema-foundation/README.md b/packages/overture-schema-foundation/README.md new file mode 100644 index 000000000..33764e38e --- /dev/null +++ b/packages/overture-schema-foundation/README.md @@ -0,0 +1 @@ +todo: README diff --git a/packages/overture-schema-foundation/pyproject.toml b/packages/overture-schema-foundation/pyproject.toml new file mode 100644 index 000000000..c61c2d82e --- /dev/null +++ b/packages/overture-schema-foundation/pyproject.toml @@ -0,0 +1,49 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "overture-schema-foundation" +dynamic = ["version"] +description = "Foundational types at the base of the Overture Maps schema system" +readme = "README.md" +requires-python = ">=3.10" +license = "MIT" +dependencies = [ + "pydantic>=2.0.0", + "shapely>=2.0.0", +] + +[dependency-groups] +dev = [ + "pytest>=7.0", + "ruff", + "mypy", +] + +[tool.hatch.version] +path = "src/overture/schema/foundation/__about__.py" + +[tool.hatch.build.targets.wheel] +packages = ["src/overture"] + +[tool.ruff] +target-version = "py310" +line-length = 88 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade +] +ignore = [ + "E501", # line too long + "B008", # do not perform function calls in argument defaults + "C901", # too complex +] +per-file-ignores = {"__init__.py" = ["F401"]} diff --git a/packages/overture-schema-foundation/src/overture/__init__.py b/packages/overture-schema-foundation/src/overture/__init__.py new file mode 100644 index 000000000..8db66d3d0 --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/__init__.py @@ -0,0 +1 @@ +__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/packages/overture-schema-foundation/src/overture/schema/__init__.py b/packages/overture-schema-foundation/src/overture/schema/__init__.py new file mode 100644 index 000000000..8db66d3d0 --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/__init__.py @@ -0,0 +1 @@ +__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/__about__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/__about__.py new file mode 100644 index 000000000..3dc1f76bc --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/__about__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/__init__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/__init__.py new file mode 100644 index 000000000..2c131d215 --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/__init__.py @@ -0,0 +1,21 @@ +""" +foundation +========== +Foundational types at the base of the Overture Maps schema system. + +A set of primitive types, constraint rules, and Pydantic base model classes that +can be used to create strongly-typed, predictably validated, data. + +Subpackages +----------- +primitive : module + Contains primitive data types that can be used as fields in Pydantic models. + + + +Examples +-------- +Basic usage: + +todo: Vic return to this. +""" diff --git a/packages/overture-schema-core/src/overture/schema/core/geometry.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geometry.py similarity index 100% rename from packages/overture-schema-core/src/overture/schema/core/geometry.py rename to packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geometry.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/py.typed b/packages/overture-schema-foundation/src/overture/schema/foundation/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/packages/overture-schema-core/tests/test_geometry.py b/packages/overture-schema-foundation/tests/primitive/test_geometry.py similarity index 99% rename from packages/overture-schema-core/tests/test_geometry.py rename to packages/overture-schema-foundation/tests/primitive/test_geometry.py index f6f48b2ca..84ae51b63 100644 --- a/packages/overture-schema-core/tests/test_geometry.py +++ b/packages/overture-schema-foundation/tests/primitive/test_geometry.py @@ -5,11 +5,16 @@ from typing import Annotated, Any import pytest -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from pydantic import BaseModel, ValidationError from pytest_subtests import SubTests from shapely import wkt +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) + def test_geometry_type_sorted() -> None: enumerated_order = tuple(GeometryType) diff --git a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py index 19b36d9ec..6a4184912 100644 --- a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py +++ b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py @@ -8,7 +8,6 @@ Feature, StrictBaseModel, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import ( Address, Named, @@ -21,6 +20,11 @@ from overture.schema.core.validation import ( UniqueItemsConstraint, ) +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..types import PlaceCategory from .enums import OperatingStatus diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py index b618e571c..6db452944 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py @@ -7,7 +7,11 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) class Connector(Feature[Literal["transportation"], Literal["connector"]]): diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py index 603d4e966..b7e3afd9e 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py @@ -7,14 +7,18 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.geometry import Geometry, GeometryType, GeometryTypeConstraint from overture.schema.core.models import ( Named, ) from overture.schema.core.validation import UniqueItemsConstraint -from overture.schema.transportation.models import ConnectorReference +from overture.schema.foundation.primitive.geometry import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) from ..enums import RailClass, RoadClass, Subclass, Subtype +from ..models import ConnectorReference from ..types import ( AccessRules, Destinations, diff --git a/packages/overture-schema-transportation-theme/tests/test_hashability.py b/packages/overture-schema-transportation-theme/tests/test_hashability.py index 837045b0b..68259d97f 100644 --- a/packages/overture-schema-transportation-theme/tests/test_hashability.py +++ b/packages/overture-schema-transportation-theme/tests/test_hashability.py @@ -1,7 +1,7 @@ """Tests for hashability of all scope and related classes.""" -from overture.schema.core.geometry import Geometry from overture.schema.core.models import GeometricRangeScope +from overture.schema.foundation.primitive.geometry import Geometry from overture.schema.transportation.enums import ( AccessType, DestinationLabelType, diff --git a/uv.lock b/uv.lock index 4bb661a84..5c355f519 100644 --- a/uv.lock +++ b/uv.lock @@ -14,6 +14,7 @@ members = [ "overture-schema-buildings-theme", "overture-schema-core", "overture-schema-divisions-theme", + "overture-schema-foundation", "overture-schema-places-theme", "overture-schema-sources", "overture-schema-transportation-theme", @@ -603,6 +604,34 @@ requires-dist = [ { name = "pydantic", specifier = ">=2.0" }, ] +[[package]] +name = "overture-schema-foundation" +source = { editable = "packages/overture-schema-foundation" } +dependencies = [ + { name = "pydantic" }, + { name = "shapely" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "shapely", specifier = ">=2.0.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pytest", specifier = ">=7.0" }, + { name = "ruff" }, +] + [[package]] name = "overture-schema-places-theme" source = { editable = "packages/overture-schema-places-theme" } From 4320c1f3b574fbb5059c6a1a19dc63e7f51d103d Mon Sep 17 00:00:00 2001 From: schapper Date: Tue, 30 Sep 2025 17:24:53 -0700 Subject: [PATCH 02/20] wip - merge to pydantic main, making a huge mess along the way This commit used to move "sources" out of the way for now, but that's over now after rebasing on Seth's #394. --- .../overture/schema/base/bathymetry/models.py | 2 +- .../overture/schema/base/land_cover/models.py | 2 +- .../overture-schema-core/tests/test_models.py | 71 +- packages/overture-schema-sources/README.md | 23 - .../src/overture/__init__.py | 1 - .../src/overture/schema/__about__.py | 1 - .../src/overture/schema/__init__.py | 1 - .../src/overture/schema/py.typed | 0 .../src/overture/schema/sources/__init__.py | 7 - .../src/overture/schema/sources/__main__.py | 122 -- .../src/overture/schema/sources/enums.py | 15 - .../src/overture/schema/sources/models.py | 180 --- .../src/overture/schema/sources/types.py | 11 - .../tests/sources_baseline_schema.json | 297 ----- .../test_sources_json_schema_baseline.py | 28 - reference/examples/sources/basic-sources.yaml | 70 - reference/examples/sources/minimal.yaml | 17 - uv.lock | 1135 ----------------- 18 files changed, 3 insertions(+), 1980 deletions(-) delete mode 100644 packages/overture-schema-sources/README.md delete mode 100644 packages/overture-schema-sources/src/overture/__init__.py delete mode 100644 packages/overture-schema-sources/src/overture/schema/__about__.py delete mode 100644 packages/overture-schema-sources/src/overture/schema/__init__.py delete mode 100644 packages/overture-schema-sources/src/overture/schema/py.typed delete mode 100644 packages/overture-schema-sources/src/overture/schema/sources/__init__.py delete mode 100644 packages/overture-schema-sources/src/overture/schema/sources/__main__.py delete mode 100644 packages/overture-schema-sources/src/overture/schema/sources/enums.py delete mode 100644 packages/overture-schema-sources/src/overture/schema/sources/models.py delete mode 100644 packages/overture-schema-sources/src/overture/schema/sources/types.py delete mode 100644 packages/overture-schema-sources/tests/sources_baseline_schema.json delete mode 100644 packages/overture-schema-sources/tests/test_sources_json_schema_baseline.py delete mode 100644 reference/examples/sources/basic-sources.yaml delete mode 100644 reference/examples/sources/minimal.yaml delete mode 100644 uv.lock diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py index 4e4eeffda..ecb8242db 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py @@ -8,7 +8,7 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.models import CartographicallyHinted, Stacked +from overture.schema.core.models import CartographicallyHinted from overture.schema.foundation.primitive.geometry import ( Geometry, GeometryType, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py index 29a4059b3..a5e574503 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py @@ -8,7 +8,7 @@ from overture.schema.core import ( Feature, ) -from overture.schema.core.models import CartographicallyHinted, Stacked +from overture.schema.core.models import CartographicallyHinted from overture.schema.foundation.primitive.geometry import ( Geometry, GeometryType, diff --git a/packages/overture-schema-core/tests/test_models.py b/packages/overture-schema-core/tests/test_models.py index 0dcc0f9d2..a56730805 100644 --- a/packages/overture-schema-core/tests/test_models.py +++ b/packages/overture-schema-core/tests/test_models.py @@ -4,6 +4,7 @@ import pytest from deepdiff import DeepDiff from overture.schema.core.bbox import BBox +from overture.schema.core.json_schema import EnhancedJsonSchemaGenerator from overture.schema.core.models import Feature from overture.schema.foundation.primitive.geometry import Geometry from shapely.geometry import LineString, Point @@ -39,7 +40,6 @@ def test_feature_json_schema() -> None: "additionalProperties": False, "properties": { "between": { -<<<<<<< HEAD "items": { "maximum": 1.0, "minimum": 0.0, @@ -61,47 +61,6 @@ def test_feature_json_schema() -> None: "type": "string", }, "confidence": {"maximum": 1.0, "minimum": 0.0, "type": "number"}, -======= - "anyOf": [ - { - "items": { - "maximum": 1.0, - "minimum": 0.0, - "type": "number", - }, - "maxItems": 2, - "minItems": 2, - "type": "array", - }, - {"type": "null"}, - ], - "default": None, - }, - "property": {"type": "string"}, - "dataset": {"type": "string"}, - "record_id": { - "anyOf": [{"type": "string"}, {"type": "null"}], - "default": None, - }, - "update_time": { - "anyOf": [ - { - "format": "date-time", - "pattern": "^([1-9]\\d{3})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])T([01]\\d|2[0-3]):([0-5]\\d):([0-5]\\d|60)(\\.\\d{1,3})?(Z|[-+]([01]\\d|2[0-3]):[0-5]\\d)$", - "type": "string", - }, - {"type": "null"}, - ], - "default": None, - }, - "confidence": { - "anyOf": [ - {"maximum": 1.0, "minimum": 0.0, "type": "number"}, - {"type": "null"}, - ], - "default": None, - }, ->>>>>>> cdf76fd (wip - repackage/modularize) }, "required": ["property", "dataset"], "type": "object", @@ -438,50 +397,22 @@ def test_feature_json_schema() -> None: ], }, "bbox": { -<<<<<<< HEAD "items": {"type": "number"}, "maxItems": 4, "minItems": 4, "type": "array", -======= - "anyOf": [ - { - "items": {"type": "number"}, - "maxItems": 4, - "minItems": 4, - "type": "array", - }, - {"type": "null"}, - ], - "default": None, ->>>>>>> cdf76fd (wip - repackage/modularize) }, "properties": { "type": "object", "properties": { "theme": {"type": "string"}, "type": {"type": "string"}, -<<<<<<< HEAD "version": {"maximum": 2147483647, "minimum": 0, "type": "integer"}, "sources": { "items": {"$ref": "#/$defs/SourcePropertyItem"}, "minItems": 1, "type": "array", "uniqueItems": True, -======= - "version": {"minimum": 0, "type": "integer"}, - "sources": { - "anyOf": [ - { - "items": {"$ref": "#/$defs/SourcePropertyItem"}, - "minItems": 1, - "type": "array", - "uniqueItems": True, - }, - {"type": "null"}, - ], - "default": None, ->>>>>>> cdf76fd (wip - repackage/modularize) }, }, "unevaluatedProperties": False, diff --git a/packages/overture-schema-sources/README.md b/packages/overture-schema-sources/README.md deleted file mode 100644 index 2000ea7ad..000000000 --- a/packages/overture-schema-sources/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Overture Schema Sources - -Shared models and validation tools for the data sources catalog used across -Overture Maps themes. - -## Contents - -- `overture.schema.sources` module, exporting the `Sources` aggregate model - and related dataset structures. -- CLI helper (`python -m overture.schema.sources`) to validate sources JSON - files against the schema. -- Reference examples and counterexamples under `schema/reference` illustrating - valid and invalid source structures. - -## Usage - -```bash -uv run python -m overture.schema.sources path/to/sources.json -``` - -This validates the payload and reports schema violations with dataset-level -context. The package also registers an `overture.models` entry point for the -`Sources` model so tools can discover the schema automatically. \ No newline at end of file diff --git a/packages/overture-schema-sources/src/overture/__init__.py b/packages/overture-schema-sources/src/overture/__init__.py deleted file mode 100644 index 8db66d3d0..000000000 --- a/packages/overture-schema-sources/src/overture/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/packages/overture-schema-sources/src/overture/schema/__about__.py b/packages/overture-schema-sources/src/overture/schema/__about__.py deleted file mode 100644 index 3dc1f76bc..000000000 --- a/packages/overture-schema-sources/src/overture/schema/__about__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.1.0" diff --git a/packages/overture-schema-sources/src/overture/schema/__init__.py b/packages/overture-schema-sources/src/overture/schema/__init__.py deleted file mode 100644 index 8db66d3d0..000000000 --- a/packages/overture-schema-sources/src/overture/schema/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/packages/overture-schema-sources/src/overture/schema/py.typed b/packages/overture-schema-sources/src/overture/schema/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/overture-schema-sources/src/overture/schema/sources/__init__.py b/packages/overture-schema-sources/src/overture/schema/sources/__init__.py deleted file mode 100644 index 8dddb62b3..000000000 --- a/packages/overture-schema-sources/src/overture/schema/sources/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Sources schema package.""" - -__path__ = __import__("pkgutil").extend_path(__path__, __name__) - -from .models import Sources - -__all__ = ["Sources"] diff --git a/packages/overture-schema-sources/src/overture/schema/sources/__main__.py b/packages/overture-schema-sources/src/overture/schema/sources/__main__.py deleted file mode 100644 index 952869abe..000000000 --- a/packages/overture-schema-sources/src/overture/schema/sources/__main__.py +++ /dev/null @@ -1,122 +0,0 @@ -"""CLI module for validating Sources JSON files. - -Usage: - uv run python -m overture.schema.sources -""" - -import json -import sys -from pathlib import Path - -from pydantic import ValidationError - -from .models import Sources - - -def main() -> None: - """Main function for CLI validation.""" - if len(sys.argv) != 2: - print( - "Usage: uv run python -m overture.schema.sources ", - file=sys.stderr, - ) - sys.exit(1) - - json_file_path = Path(sys.argv[1]) - - if not json_file_path.exists(): - print(f"Error: File '{json_file_path}' does not exist.", file=sys.stderr) - sys.exit(1) - - if not json_file_path.is_file(): - print(f"Error: '{json_file_path}' is not a file.", file=sys.stderr) - sys.exit(1) - - try: - # Read and parse the JSON file - with json_file_path.open("r", encoding="utf-8") as f: - data = json.load(f) - - # Validate using Pydantic model - sources = Sources.model_validate(data) - - print(f"✓ Successfully validated {json_file_path}") - print(f" - {len(sources.datasets)} datasets") - print(f" - {len(sources.license_priority)} license priorities") - - except json.JSONDecodeError as e: - print(f"Error: Invalid JSON in '{json_file_path}': {e}", file=sys.stderr) - sys.exit(1) - - except ValidationError as e: - print(f"Error: Validation failed for '{json_file_path}':", file=sys.stderr) - - datasets = None - if isinstance(data, dict): - datasets = data.get("datasets") - - def source_name_for_error(loc: tuple[int | str, ...]) -> str | None: - if not loc: - return None - if loc[0] != "datasets" or len(loc) < 2: - return None - - dataset_index = loc[1] - if not isinstance(dataset_index, int): - return None - - if not isinstance(datasets, list): - return None - if dataset_index < 0 or dataset_index >= len(datasets): - return None - - dataset = datasets[dataset_index] - if not isinstance(dataset, dict): - return None - - source_name = dataset.get("source_name") - if isinstance(source_name, str) and source_name: - return source_name - - return None - - def format_field_path(loc: tuple[int | str, ...]) -> str: - if len(loc) <= 2: - return "" - - field_parts = [] - for part in loc[2:]: - if isinstance(part, str): - if "[" in part or "(" in part or ")" in part: - continue - field_parts.append(part) - # Ignore numeric indices in the path to keep output concise - - return ".".join(field_parts) - - for error in e.errors(): - loc = error["loc"] - source_name = source_name_for_error(loc) - - if source_name: - field_path = format_field_path(loc) - if field_path: - identifier = f"{source_name}: {field_path}" - else: - identifier = source_name - else: - identifier = " -> ".join(str(part) for part in loc) - - print(f" {identifier}: {error['msg']}", file=sys.stderr) - sys.exit(1) - - except Exception as e: - print( - f"Error: Unexpected error processing '{json_file_path}': {e}", - file=sys.stderr, - ) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/packages/overture-schema-sources/src/overture/schema/sources/enums.py b/packages/overture-schema-sources/src/overture/schema/sources/enums.py deleted file mode 100644 index 0ed0188ba..000000000 --- a/packages/overture-schema-sources/src/overture/schema/sources/enums.py +++ /dev/null @@ -1,15 +0,0 @@ -from enum import Enum - - -class BuildSource(str, Enum): - """The ingest source for address data.""" - - OPEN_ADDRESSES = "OpenAddresses" - TF_DATA_PLATFORM = "tf-data-platform" - - -class UpdateType(str, Enum): - """Whether the data is continuously updated upstream or needs manual intervention.""" - - CONTINUOUS = "continuous" - MANUAL = "manual" diff --git a/packages/overture-schema-sources/src/overture/schema/sources/models.py b/packages/overture-schema-sources/src/overture/schema/sources/models.py deleted file mode 100644 index 197986c10..000000000 --- a/packages/overture-schema-sources/src/overture/schema/sources/models.py +++ /dev/null @@ -1,180 +0,0 @@ -"""Sources schema models for Overture Maps data sources.""" - -from datetime import date -from typing import Annotated, Literal - -from pydantic import Field, HttpUrl - -from overture.schema.core.models import StrictBaseModel -from overture.schema.core.types import CountryCode - -from .enums import BuildSource, UpdateType -from .types import LicenseShortname - - -class Dataset(StrictBaseModel): - """Dataset definition for Overture Maps data sources.""" - - # Required - - source_name: Annotated[ - str, - Field(description="The name of the source."), - ] - source_dataset_name: Annotated[ - str, - Field( - description="The name of the dataset being used from the source. This should match the 'dataset' value found in a record's sources column." - ), - ] - data_url: Annotated[ - HttpUrl | Literal[""], - Field( - description="The data page or data portal of this source, typically includes links to data downloads and license links.", - ), - ] - data_url_archived: Annotated[ - HttpUrl | Literal[""], - Field( - description="URL of the source's data page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", - ), - ] - license_url: Annotated[ - HttpUrl | Literal[""], - Field( - description="A link to this source's data license or page referencing the license associated with the data being imported. This should include explicit license terms.", - ), - ] - license_url_archived: Annotated[ - HttpUrl | Literal[""], - Field( - description="URL of the source's license page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", - ), - ] - license_type: Annotated[ - str, - Field( - description="The license that is associated with the data being used from this source. This should be a valid SPDX license identifier when available." - ), - ] - license_text: Annotated[ - str, - Field( - description="Any relevant license text, direct from the source's license page." - ), - ] - license_attribution: Annotated[ - str, - Field(description="Any attribution required by this source."), - ] - coverage_bbox: Annotated[ - list[float], - Field( - description="The bounding box, in [xmin, ymin, xmax, ymax] format, of this source's coverage.", - min_length=4, - max_length=4, - ), - ] - - # Optional - - inception_date: Annotated[ - date | None, - Field( - description="The first date this source was used in the Overture addresses theme, in YYYY-MM-DD format." - ), - ] = None - url: Annotated[ - HttpUrl | None, - Field(description="The home page of this source."), - ] = None - url_archived: Annotated[ - HttpUrl | None, - Field( - description="URL of the source's home page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", - ), - ] = None - data_download_url: Annotated[ - list[HttpUrl | Literal[""]] | None, - Field( - description="Either a direct download link of data from this source, typically a geo-format or compressed file, or an endpoint from where the data was obtained for use within Overture." - ), - ] = None - countries: Annotated[ - list[CountryCode | Literal["Global"]] | None, - Field( - description="A list of two-character iso country codes that this data source provides data in." - ), - ] = None - coverage_description: Annotated[ - str | None, - Field( - description="A description of the coverage type of the source data - i.e. national, regional, local." - ), - ] = None - data_layer_name: Annotated[ - str | None, - Field(description="Name of the data layer used from this source."), - ] = None - oa_path: Annotated[ - list[str] | None, - Field(description="File path and name in OpenAddresses, if existing."), - ] = None - address_levels: Annotated[ - list[str] | None, - Field( - description="Available address level attributes from OpenAddress, if existing." - ), - ] = None - file_format: Annotated[ - str | None, - Field(description="Format of the file used from this source."), - ] = None - update_frequency: Annotated[ - str | None, - Field(description="How frequently the source data is updated upstream."), - ] = None - build_source: BuildSource | None = None - update_type: UpdateType | None = None - update_schedule: Annotated[ - list[str] | None, - Field( - description="The month or months in which the data is to be re-ingested by the Overture theme using this data source." - ), - ] = None - known_issues: Annotated[ - str | None, - Field( - description="A description of any issues with the data that are known - i.e. data is incomplete, coverage is incomplete, or issues with character encoding." - ), - ] = None - notes: Annotated[ - str | None, - Field( - description="Freeform notes about this data source, including notes on any pre-processing requirements." - ), - ] = None - requires_attribution: Annotated[ - str | None, # TODO should this be a bool? - Field( - description="Whether this source requires attribution to be used in Overture Maps." - ), - ] = None - - -class Sources(StrictBaseModel): - """Common schema definitions for data sources.""" - - # Required - - datasets: Annotated[ - list[Dataset], - Field(description="List of data source entries used by Overture."), - ] - license_priority: Annotated[ - dict[LicenseShortname, Annotated[int, Field(ge=0)]], - Field( - description="Map of license shortnames to their priority (lower number indicates higher priority)." - ), - Field(json_schema_extra={"additionalProperties": False}), - ] diff --git a/packages/overture-schema-sources/src/overture/schema/sources/types.py b/packages/overture-schema-sources/src/overture/schema/sources/types.py deleted file mode 100644 index 64dc3626a..000000000 --- a/packages/overture-schema-sources/src/overture/schema/sources/types.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import Annotated, NewType - -from pydantic import Field - -LicenseShortname = NewType( - "LicenseShortname", - Annotated[ - str, - Field(pattern=r"^[A-Za-z0-9._+\-]+$"), - ], -) diff --git a/packages/overture-schema-sources/tests/sources_baseline_schema.json b/packages/overture-schema-sources/tests/sources_baseline_schema.json deleted file mode 100644 index f39b00922..000000000 --- a/packages/overture-schema-sources/tests/sources_baseline_schema.json +++ /dev/null @@ -1,297 +0,0 @@ -{ - "$defs": { - "BuildSource": { - "description": "The ingest source for address data.", - "enum": [ - "OpenAddresses", - "tf-data-platform" - ], - "title": "BuildSource", - "type": "string" - }, - "Dataset": { - "additionalProperties": false, - "description": "Dataset definition for Overture Maps data sources.", - "properties": { - "address_levels": { - "description": "Available address level attributes from OpenAddress, if existing.", - "items": { - "type": "string" - }, - "title": "Address Levels", - "type": "array" - }, - "build_source": { - "$ref": "#/$defs/BuildSource" - }, - "countries": { - "description": "A list of two-character iso country codes that this data source provides data in.", - "items": { - "anyOf": [ - { - "description": "ISO 3166-1 alpha-2 country code", - "maxLength": 2, - "minLength": 2, - "pattern": "^[A-Z]{2}$", - "type": "string" - }, - { - "const": "Global", - "type": "string" - } - ] - }, - "title": "Countries", - "type": "array" - }, - "coverage_bbox": { - "description": "The bounding box, in [xmin, ymin, xmax, ymax] format, of this source's coverage.", - "items": { - "type": "number" - }, - "maxItems": 4, - "minItems": 4, - "title": "Coverage Bbox", - "type": "array" - }, - "coverage_description": { - "description": "A description of the coverage type of the source data - i.e. national, regional, local.", - "title": "Coverage Description", - "type": "string" - }, - "data_download_url": { - "description": "Either a direct download link of data from this source, typically a geo-format or compressed file, or an endpoint from where the data was obtained for use within Overture.", - "items": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "const": "", - "type": "string" - } - ] - }, - "title": "Data Download Url", - "type": "array" - }, - "data_layer_name": { - "description": "Name of the data layer used from this source.", - "title": "Data Layer Name", - "type": "string" - }, - "data_url": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "const": "", - "type": "string" - } - ], - "description": "The data page or data portal of this source, typically includes links to data downloads and license links.", - "title": "Data Url" - }, - "data_url_archived": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "const": "", - "type": "string" - } - ], - "description": "URL of the source's data page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", - "title": "Data Url Archived" - }, - "file_format": { - "description": "Format of the file used from this source.", - "title": "File Format", - "type": "string" - }, - "inception_date": { - "description": "The first date this source was used in the Overture addresses theme, in YYYY-MM-DD format.", - "format": "date", - "title": "Inception Date", - "type": "string" - }, - "known_issues": { - "description": "A description of any issues with the data that are known - i.e. data is incomplete, coverage is incomplete, or issues with character encoding.", - "title": "Known Issues", - "type": "string" - }, - "license_attribution": { - "description": "Any attribution required by this source.", - "title": "License Attribution", - "type": "string" - }, - "license_text": { - "description": "Any relevant license text, direct from the source's license page.", - "title": "License Text", - "type": "string" - }, - "license_type": { - "description": "The license that is associated with the data being used from this source. This should be a valid SPDX license identifier when available.", - "title": "License Type", - "type": "string" - }, - "license_url": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "const": "", - "type": "string" - } - ], - "description": "A link to this source's data license or page referencing the license associated with the data being imported. This should include explicit license terms.", - "title": "License Url" - }, - "license_url_archived": { - "anyOf": [ - { - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "type": "string" - }, - { - "const": "", - "type": "string" - } - ], - "description": "URL of the source's license page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", - "title": "License Url Archived" - }, - "notes": { - "description": "Freeform notes about this data source, including notes on any pre-processing requirements.", - "title": "Notes", - "type": "string" - }, - "oa_path": { - "description": "File path and name in OpenAddresses, if existing.", - "items": { - "type": "string" - }, - "title": "Oa Path", - "type": "array" - }, - "requires_attribution": { - "description": "Whether this source requires attribution to be used in Overture Maps.", - "title": "Requires Attribution", - "type": "string" - }, - "source_dataset_name": { - "description": "The name of the dataset being used from the source. This should match the 'dataset' value found in a record's sources column.", - "title": "Source Dataset Name", - "type": "string" - }, - "source_name": { - "description": "The name of the source.", - "title": "Source Name", - "type": "string" - }, - "update_frequency": { - "description": "How frequently the source data is updated upstream.", - "title": "Update Frequency", - "type": "string" - }, - "update_schedule": { - "description": "The month or months in which the data is to be re-ingested by the Overture theme using this data source.", - "items": { - "type": "string" - }, - "title": "Update Schedule", - "type": "array" - }, - "update_type": { - "$ref": "#/$defs/UpdateType" - }, - "url": { - "description": "The home page of this source.", - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "title": "Url", - "type": "string" - }, - "url_archived": { - "description": "URL of the source's home page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", - "format": "uri", - "maxLength": 2083, - "minLength": 1, - "title": "Url Archived", - "type": "string" - } - }, - "required": [ - "source_name", - "source_dataset_name", - "data_url", - "data_url_archived", - "license_url", - "license_url_archived", - "license_type", - "license_text", - "license_attribution", - "coverage_bbox" - ], - "title": "Dataset", - "type": "object" - }, - "UpdateType": { - "description": "Whether the data is continuously updated upstream or needs manual intervention.", - "enum": [ - "continuous", - "manual" - ], - "title": "UpdateType", - "type": "string" - } - }, - "additionalProperties": false, - "description": "Common schema definitions for data sources.", - "properties": { - "datasets": { - "description": "List of data source entries used by Overture.", - "items": { - "$ref": "#/$defs/Dataset" - }, - "title": "Datasets", - "type": "array" - }, - "license_priority": { - "additionalProperties": false, - "description": "Map of license shortnames to their priority (lower number indicates higher priority).", - "patternProperties": { - "^[A-Za-z0-9._+\\-]+$": { - "minimum": 0, - "type": "integer" - } - }, - "title": "License Priority", - "type": "object" - } - }, - "required": [ - "datasets", - "license_priority" - ], - "title": "Sources", - "type": "object" -} \ No newline at end of file diff --git a/packages/overture-schema-sources/tests/test_sources_json_schema_baseline.py b/packages/overture-schema-sources/tests/test_sources_json_schema_baseline.py deleted file mode 100644 index 20242fe53..000000000 --- a/packages/overture-schema-sources/tests/test_sources_json_schema_baseline.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Baseline JSON Schema tests for sources model.""" - -import json -import os - -from overture.schema.core import json_schema -from overture.schema.sources import Sources - - -def test_sources_json_schema_baseline() -> None: - """Ensure Sources model JSON Schema matches the committed baseline.""" - schema = json_schema(Sources) - - baseline_file = os.path.join( - os.path.dirname(__file__), "sources_baseline_schema.json" - ) - - if not os.path.exists(baseline_file): - with open(baseline_file, "w", encoding="utf-8") as f: - json.dump(schema, f, indent=2, sort_keys=True) - - with open(baseline_file, encoding="utf-8") as f: - baseline_schema = json.load(f) - - assert schema == baseline_schema, ( - "Generated JSON Schema differs from baseline. " - "If this change is intentional, delete the baseline file to regenerate it." - ) diff --git a/reference/examples/sources/basic-sources.yaml b/reference/examples/sources/basic-sources.yaml deleted file mode 100644 index 01c7b6520..000000000 --- a/reference/examples/sources/basic-sources.yaml +++ /dev/null @@ -1,70 +0,0 @@ -datasets: - - source_name: Example City Open Data - source_dataset_name: address_points - data_url: https://data.example.gov/datasets/address-points - data_url_archived: https://web.archive.org/web/2025/https://data.example.gov/datasets/address-points - license_url: https://data.example.gov/license - license_url_archived: https://web.archive.org/web/2025/https://data.example.gov/license - license_type: CC-BY-4.0 - license_text: | - The City of Example grants permission to use this dataset under the terms of the Creative Commons Attribution 4.0 International License. - license_attribution: City of Example GIS Department - coverage_bbox: - - -122.79 - - 45.43 - - -122.45 - - 45.65 - inception_date: '2022-08-01' - url: https://example.gov - url_archived: https://web.archive.org/web/2025/https://example.gov - data_download_url: - - https://data.example.gov/downloads/address_points.geojson - countries: - - US - coverage_description: Citywide coverage - data_layer_name: address_points - oa_path: - - us/example_city/addresses.csv - address_levels: - - address - file_format: CSV - update_frequency: monthly - build_source: OpenAddresses - update_type: continuous - update_schedule: - - January - - July - known_issues: Source omits new subdivisions until quarterly update. - notes: Dataset normalized to Overture schema during ingest. - requires_attribution: Yes - - source_name: Example Parcel Authority - source_dataset_name: parcel_boundaries - data_url: https://parcels.example.gov/downloads/boundaries - data_url_archived: https://web.archive.org/web/2025/https://parcels.example.gov/downloads/boundaries - license_url: https://parcels.example.gov/license - license_url_archived: https://web.archive.org/web/2025/https://parcels.example.gov/license - license_type: ODbL-1.0 - license_text: | - This dataset is available under the Open Database License v1.0. - license_attribution: Example Parcel Authority - coverage_bbox: - - -122.81 - - 45.38 - - -122.37 - - 45.72 - url: https://parcels.example.gov - data_download_url: - - https://parcels.example.gov/downloads/parcels.gpkg - countries: - - US - coverage_description: Countywide parcel coverage - file_format: GPKG - update_frequency: quarterly - build_source: tf-data-platform - update_type: manual - known_issues: Parcel ownership attributes excluded from public release. - notes: Simplified geometry provided for efficient rendering. - requires_attribution: Required in downstream products. -license_priority: - CC-BY-4.0: 1 - ODbL-1.0: 2 diff --git a/reference/examples/sources/minimal.yaml b/reference/examples/sources/minimal.yaml deleted file mode 100644 index a48862f7e..000000000 --- a/reference/examples/sources/minimal.yaml +++ /dev/null @@ -1,17 +0,0 @@ -datasets: - - source_name: Address Sample - source_dataset_name: addresses_sample - data_url: https://example.com/addresses - data_url_archived: https://web.archive.org/web/2024/https://example.com/addresses - license_url: https://example.com/addresses/license - license_url_archived: https://web.archive.org/web/2024/https://example.com/addresses/license - license_type: CC-BY-4.0 - license_text: This sample dataset is released under CC-BY-4.0. - license_attribution: Addresses Sample Contributors - coverage_bbox: - - -10.0 - - 50.0 - - -9.5 - - 50.5 -license_priority: - CC-BY-4.0: 1 diff --git a/uv.lock b/uv.lock deleted file mode 100644 index 5c355f519..000000000 --- a/uv.lock +++ /dev/null @@ -1,1135 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.10" -resolution-markers = [ - "python_full_version >= '3.11'", - "python_full_version < '3.11'", -] - -[manifest] -members = [ - "overture-schema", - "overture-schema-addresses-theme", - "overture-schema-base-theme", - "overture-schema-buildings-theme", - "overture-schema-core", - "overture-schema-divisions-theme", - "overture-schema-foundation", - "overture-schema-places-theme", - "overture-schema-sources", - "overture-schema-transportation-theme", - "overture-schema-validation", - "overture-schema-workspace", -] - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, -] - -[[package]] -name = "attrs" -version = "25.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, - { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, - { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, - { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, - { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "coverage" -version = "7.9.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/b7/c0465ca253df10a9e8dae0692a4ae6e9726d245390aaef92360e1d6d3832/coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b", size = 813556, upload-time = "2025-07-03T10:54:15.101Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/0d/5c2114fd776c207bd55068ae8dc1bef63ecd1b767b3389984a8e58f2b926/coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912", size = 212039, upload-time = "2025-07-03T10:52:38.955Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ad/dc51f40492dc2d5fcd31bb44577bc0cc8920757d6bc5d3e4293146524ef9/coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f", size = 212428, upload-time = "2025-07-03T10:52:41.36Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a3/55cb3ff1b36f00df04439c3993d8529193cdf165a2467bf1402539070f16/coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f", size = 241534, upload-time = "2025-07-03T10:52:42.956Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c9/a8410b91b6be4f6e9c2e9f0dce93749b6b40b751d7065b4410bf89cb654b/coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf", size = 239408, upload-time = "2025-07-03T10:52:44.199Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c4/6f3e56d467c612b9070ae71d5d3b114c0b899b5788e1ca3c93068ccb7018/coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547", size = 240552, upload-time = "2025-07-03T10:52:45.477Z" }, - { url = "https://files.pythonhosted.org/packages/fd/20/04eda789d15af1ce79bce5cc5fd64057c3a0ac08fd0576377a3096c24663/coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45", size = 240464, upload-time = "2025-07-03T10:52:46.809Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5a/217b32c94cc1a0b90f253514815332d08ec0812194a1ce9cca97dda1cd20/coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2", size = 239134, upload-time = "2025-07-03T10:52:48.149Z" }, - { url = "https://files.pythonhosted.org/packages/34/73/1d019c48f413465eb5d3b6898b6279e87141c80049f7dbf73fd020138549/coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e", size = 239405, upload-time = "2025-07-03T10:52:49.687Z" }, - { url = "https://files.pythonhosted.org/packages/49/6c/a2beca7aa2595dad0c0d3f350382c381c92400efe5261e2631f734a0e3fe/coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e", size = 214519, upload-time = "2025-07-03T10:52:51.036Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c8/91e5e4a21f9a51e2c7cdd86e587ae01a4fcff06fc3fa8cde4d6f7cf68df4/coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c", size = 215400, upload-time = "2025-07-03T10:52:52.313Z" }, - { url = "https://files.pythonhosted.org/packages/39/40/916786453bcfafa4c788abee4ccd6f592b5b5eca0cd61a32a4e5a7ef6e02/coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba", size = 212152, upload-time = "2025-07-03T10:52:53.562Z" }, - { url = "https://files.pythonhosted.org/packages/9f/66/cc13bae303284b546a030762957322bbbff1ee6b6cb8dc70a40f8a78512f/coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa", size = 212540, upload-time = "2025-07-03T10:52:55.196Z" }, - { url = "https://files.pythonhosted.org/packages/0f/3c/d56a764b2e5a3d43257c36af4a62c379df44636817bb5f89265de4bf8bd7/coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a", size = 245097, upload-time = "2025-07-03T10:52:56.509Z" }, - { url = "https://files.pythonhosted.org/packages/b1/46/bd064ea8b3c94eb4ca5d90e34d15b806cba091ffb2b8e89a0d7066c45791/coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc", size = 242812, upload-time = "2025-07-03T10:52:57.842Z" }, - { url = "https://files.pythonhosted.org/packages/43/02/d91992c2b29bc7afb729463bc918ebe5f361be7f1daae93375a5759d1e28/coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2", size = 244617, upload-time = "2025-07-03T10:52:59.239Z" }, - { url = "https://files.pythonhosted.org/packages/b7/4f/8fadff6bf56595a16d2d6e33415841b0163ac660873ed9a4e9046194f779/coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c", size = 244263, upload-time = "2025-07-03T10:53:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d2/e0be7446a2bba11739edb9f9ba4eff30b30d8257370e237418eb44a14d11/coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd", size = 242314, upload-time = "2025-07-03T10:53:01.932Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7d/dcbac9345000121b8b57a3094c2dfcf1ccc52d8a14a40c1d4bc89f936f80/coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74", size = 242904, upload-time = "2025-07-03T10:53:03.478Z" }, - { url = "https://files.pythonhosted.org/packages/41/58/11e8db0a0c0510cf31bbbdc8caf5d74a358b696302a45948d7c768dfd1cf/coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6", size = 214553, upload-time = "2025-07-03T10:53:05.174Z" }, - { url = "https://files.pythonhosted.org/packages/3a/7d/751794ec8907a15e257136e48dc1021b1f671220ecccfd6c4eaf30802714/coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7", size = 215441, upload-time = "2025-07-03T10:53:06.472Z" }, - { url = "https://files.pythonhosted.org/packages/62/5b/34abcedf7b946c1c9e15b44f326cb5b0da852885312b30e916f674913428/coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62", size = 213873, upload-time = "2025-07-03T10:53:07.699Z" }, - { url = "https://files.pythonhosted.org/packages/53/d7/7deefc6fd4f0f1d4c58051f4004e366afc9e7ab60217ac393f247a1de70a/coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0", size = 212344, upload-time = "2025-07-03T10:53:09.3Z" }, - { url = "https://files.pythonhosted.org/packages/95/0c/ee03c95d32be4d519e6a02e601267769ce2e9a91fc8faa1b540e3626c680/coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3", size = 212580, upload-time = "2025-07-03T10:53:11.52Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9f/826fa4b544b27620086211b87a52ca67592622e1f3af9e0a62c87aea153a/coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1", size = 246383, upload-time = "2025-07-03T10:53:13.134Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b3/4477aafe2a546427b58b9c540665feff874f4db651f4d3cb21b308b3a6d2/coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615", size = 243400, upload-time = "2025-07-03T10:53:14.614Z" }, - { url = "https://files.pythonhosted.org/packages/f8/c2/efffa43778490c226d9d434827702f2dfbc8041d79101a795f11cbb2cf1e/coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b", size = 245591, upload-time = "2025-07-03T10:53:15.872Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e7/a59888e882c9a5f0192d8627a30ae57910d5d449c80229b55e7643c078c4/coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9", size = 245402, upload-time = "2025-07-03T10:53:17.124Z" }, - { url = "https://files.pythonhosted.org/packages/92/a5/72fcd653ae3d214927edc100ce67440ed8a0a1e3576b8d5e6d066ed239db/coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f", size = 243583, upload-time = "2025-07-03T10:53:18.781Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f5/84e70e4df28f4a131d580d7d510aa1ffd95037293da66fd20d446090a13b/coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d", size = 244815, upload-time = "2025-07-03T10:53:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/39/e7/d73d7cbdbd09fdcf4642655ae843ad403d9cbda55d725721965f3580a314/coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355", size = 214719, upload-time = "2025-07-03T10:53:21.521Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d6/7486dcc3474e2e6ad26a2af2db7e7c162ccd889c4c68fa14ea8ec189c9e9/coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0", size = 215509, upload-time = "2025-07-03T10:53:22.853Z" }, - { url = "https://files.pythonhosted.org/packages/b7/34/0439f1ae2593b0346164d907cdf96a529b40b7721a45fdcf8b03c95fcd90/coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b", size = 213910, upload-time = "2025-07-03T10:53:24.472Z" }, - { url = "https://files.pythonhosted.org/packages/94/9d/7a8edf7acbcaa5e5c489a646226bed9591ee1c5e6a84733c0140e9ce1ae1/coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038", size = 212367, upload-time = "2025-07-03T10:53:25.811Z" }, - { url = "https://files.pythonhosted.org/packages/e8/9e/5cd6f130150712301f7e40fb5865c1bc27b97689ec57297e568d972eec3c/coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d", size = 212632, upload-time = "2025-07-03T10:53:27.075Z" }, - { url = "https://files.pythonhosted.org/packages/a8/de/6287a2c2036f9fd991c61cefa8c64e57390e30c894ad3aa52fac4c1e14a8/coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3", size = 245793, upload-time = "2025-07-03T10:53:28.408Z" }, - { url = "https://files.pythonhosted.org/packages/06/cc/9b5a9961d8160e3cb0b558c71f8051fe08aa2dd4b502ee937225da564ed1/coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14", size = 243006, upload-time = "2025-07-03T10:53:29.754Z" }, - { url = "https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6", size = 244990, upload-time = "2025-07-03T10:53:31.098Z" }, - { url = "https://files.pythonhosted.org/packages/48/83/801cdc10f137b2d02b005a761661649ffa60eb173dcdaeb77f571e4dc192/coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b", size = 245157, upload-time = "2025-07-03T10:53:32.717Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a4/41911ed7e9d3ceb0ffb019e7635468df7499f5cc3edca5f7dfc078e9c5ec/coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d", size = 243128, upload-time = "2025-07-03T10:53:34.009Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/344543b71d31ac9cb00a664d5d0c9ef134a0fe87cb7d8430003b20fa0b7d/coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868", size = 244511, upload-time = "2025-07-03T10:53:35.434Z" }, - { url = "https://files.pythonhosted.org/packages/d5/81/3b68c77e4812105e2a060f6946ba9e6f898ddcdc0d2bfc8b4b152a9ae522/coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a", size = 214765, upload-time = "2025-07-03T10:53:36.787Z" }, - { url = "https://files.pythonhosted.org/packages/06/a2/7fac400f6a346bb1a4004eb2a76fbff0e242cd48926a2ce37a22a6a1d917/coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b", size = 215536, upload-time = "2025-07-03T10:53:38.188Z" }, - { url = "https://files.pythonhosted.org/packages/08/47/2c6c215452b4f90d87017e61ea0fd9e0486bb734cb515e3de56e2c32075f/coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694", size = 213943, upload-time = "2025-07-03T10:53:39.492Z" }, - { url = "https://files.pythonhosted.org/packages/a3/46/e211e942b22d6af5e0f323faa8a9bc7c447a1cf1923b64c47523f36ed488/coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5", size = 213088, upload-time = "2025-07-03T10:53:40.874Z" }, - { url = "https://files.pythonhosted.org/packages/d2/2f/762551f97e124442eccd907bf8b0de54348635b8866a73567eb4e6417acf/coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b", size = 213298, upload-time = "2025-07-03T10:53:42.218Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b7/76d2d132b7baf7360ed69be0bcab968f151fa31abe6d067f0384439d9edb/coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3", size = 256541, upload-time = "2025-07-03T10:53:43.823Z" }, - { url = "https://files.pythonhosted.org/packages/a0/17/392b219837d7ad47d8e5974ce5f8dc3deb9f99a53b3bd4d123602f960c81/coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8", size = 252761, upload-time = "2025-07-03T10:53:45.19Z" }, - { url = "https://files.pythonhosted.org/packages/d5/77/4256d3577fe1b0daa8d3836a1ebe68eaa07dd2cbaf20cf5ab1115d6949d4/coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46", size = 254917, upload-time = "2025-07-03T10:53:46.931Z" }, - { url = "https://files.pythonhosted.org/packages/53/99/fc1a008eef1805e1ddb123cf17af864743354479ea5129a8f838c433cc2c/coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584", size = 256147, upload-time = "2025-07-03T10:53:48.289Z" }, - { url = "https://files.pythonhosted.org/packages/92/c0/f63bf667e18b7f88c2bdb3160870e277c4874ced87e21426128d70aa741f/coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e", size = 254261, upload-time = "2025-07-03T10:53:49.99Z" }, - { url = "https://files.pythonhosted.org/packages/8c/32/37dd1c42ce3016ff8ec9e4b607650d2e34845c0585d3518b2a93b4830c1a/coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac", size = 255099, upload-time = "2025-07-03T10:53:51.354Z" }, - { url = "https://files.pythonhosted.org/packages/da/2e/af6b86f7c95441ce82f035b3affe1cd147f727bbd92f563be35e2d585683/coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926", size = 215440, upload-time = "2025-07-03T10:53:52.808Z" }, - { url = "https://files.pythonhosted.org/packages/4d/bb/8a785d91b308867f6b2e36e41c569b367c00b70c17f54b13ac29bcd2d8c8/coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd", size = 216537, upload-time = "2025-07-03T10:53:54.273Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a0/a6bffb5e0f41a47279fd45a8f3155bf193f77990ae1c30f9c224b61cacb0/coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb", size = 214398, upload-time = "2025-07-03T10:53:56.715Z" }, - { url = "https://files.pythonhosted.org/packages/d7/85/f8bbefac27d286386961c25515431482a425967e23d3698b75a250872924/coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050", size = 204013, upload-time = "2025-07-03T10:54:12.084Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, -] - -[[package]] -name = "deepdiff" -version = "8.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "orderly-set" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0a/0f/9cd2624f7dcd755cbf1fa21fb7234541f19a1be96a56f387ec9053ebe220/deepdiff-8.5.0.tar.gz", hash = "sha256:a4dd3529fa8d4cd5b9cbb6e3ea9c95997eaa919ba37dac3966c1b8f872dc1cd1", size = 538517, upload-time = "2025-05-09T18:44:10.035Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/3b/2e0797200c51531a6d8c97a8e4c9fa6fb56de7e6e2a15c1c067b6b10a0b0/deepdiff-8.5.0-py3-none-any.whl", hash = "sha256:d4599db637f36a1c285f5fdfc2cd8d38bde8d8be8636b65ab5e425b67c54df26", size = 85112, upload-time = "2025-05-09T18:44:07.784Z" }, -] - -[[package]] -name = "dnspython" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, -] - -[[package]] -name = "docformatter" -version = "1.7.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "charset-normalizer" }, - { name = "untokenize" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2a/7b/ee08cb5fe2627ed0b6f0cc4a1c6be6c9c71de5a3e9785de8174273fc3128/docformatter-1.7.7.tar.gz", hash = "sha256:ea0e1e8867e5af468dfc3f9e947b92230a55be9ec17cd1609556387bffac7978", size = 26587, upload-time = "2025-05-11T04:54:04.356Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl", hash = "sha256:7af49f8a46346a77858f6651f431b882c503c2f4442c8b4524b920c863277834", size = 33525, upload-time = "2025-05-11T04:54:03.353Z" }, -] - -[[package]] -name = "email-validator" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dnspython" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967, upload-time = "2024-06-20T11:30:30.034Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521, upload-time = "2024-06-20T11:30:28.248Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, -] - -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "jsonpath-ng" -version = "1.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ply" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/86/08646239a313f895186ff0a4573452038eed8c86f54380b3ebac34d32fb2/jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c", size = 37838, upload-time = "2024-10-11T15:41:42.404Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/5a/73ecb3d82f8615f32ccdadeb9356726d6cae3a4bbc840b437ceb95708063/jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6", size = 30105, upload-time = "2024-11-20T17:58:30.418Z" }, -] - -[[package]] -name = "mypy" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" }, - { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" }, - { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" }, - { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, - { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, - { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, - { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, - { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, - { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, - { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, - { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, - { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, - { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, - { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, - { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, - { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, - { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, - { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, - { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - -[[package]] -name = "numpy" -version = "2.2.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, - { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, - { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, - { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, - { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, - { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, - { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, - { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, - { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, - { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, - { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, - { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, - { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, - { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, - { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, - { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, - { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, - { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, - { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, - { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, - { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, - { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, - { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, - { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, - { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, - { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, - { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, - { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, - { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, - { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, - { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, -] - -[[package]] -name = "numpy" -version = "2.3.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/37/7d/3fec4199c5ffb892bed55cff901e4f39a58c81df9c44c280499e92cad264/numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48", size = 20489306, upload-time = "2025-07-24T21:32:07.553Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/26/1320083986108998bd487e2931eed2aeedf914b6e8905431487543ec911d/numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9", size = 21259016, upload-time = "2025-07-24T20:24:35.214Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2b/792b341463fa93fc7e55abbdbe87dac316c5b8cb5e94fb7a59fb6fa0cda5/numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168", size = 14451158, upload-time = "2025-07-24T20:24:58.397Z" }, - { url = "https://files.pythonhosted.org/packages/b7/13/e792d7209261afb0c9f4759ffef6135b35c77c6349a151f488f531d13595/numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b", size = 5379817, upload-time = "2025-07-24T20:25:07.746Z" }, - { url = "https://files.pythonhosted.org/packages/49/ce/055274fcba4107c022b2113a213c7287346563f48d62e8d2a5176ad93217/numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8", size = 6913606, upload-time = "2025-07-24T20:25:18.84Z" }, - { url = "https://files.pythonhosted.org/packages/17/f2/e4d72e6bc5ff01e2ab613dc198d560714971900c03674b41947e38606502/numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d", size = 14589652, upload-time = "2025-07-24T20:25:40.356Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b0/fbeee3000a51ebf7222016e2939b5c5ecf8000a19555d04a18f1e02521b8/numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3", size = 16938816, upload-time = "2025-07-24T20:26:05.721Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ec/2f6c45c3484cc159621ea8fc000ac5a86f1575f090cac78ac27193ce82cd/numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f", size = 16370512, upload-time = "2025-07-24T20:26:30.545Z" }, - { url = "https://files.pythonhosted.org/packages/b5/01/dd67cf511850bd7aefd6347aaae0956ed415abea741ae107834aae7d6d4e/numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097", size = 18884947, upload-time = "2025-07-24T20:26:58.24Z" }, - { url = "https://files.pythonhosted.org/packages/a7/17/2cf60fd3e6a61d006778735edf67a222787a8c1a7842aed43ef96d777446/numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220", size = 6599494, upload-time = "2025-07-24T20:27:09.786Z" }, - { url = "https://files.pythonhosted.org/packages/d5/03/0eade211c504bda872a594f045f98ddcc6caef2b7c63610946845e304d3f/numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170", size = 13087889, upload-time = "2025-07-24T20:27:29.558Z" }, - { url = "https://files.pythonhosted.org/packages/13/32/2c7979d39dafb2a25087e12310fc7f3b9d3c7d960df4f4bc97955ae0ce1d/numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89", size = 10459560, upload-time = "2025-07-24T20:27:46.803Z" }, - { url = "https://files.pythonhosted.org/packages/00/6d/745dd1c1c5c284d17725e5c802ca4d45cfc6803519d777f087b71c9f4069/numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b", size = 20956420, upload-time = "2025-07-24T20:28:18.002Z" }, - { url = "https://files.pythonhosted.org/packages/bc/96/e7b533ea5740641dd62b07a790af5d9d8fec36000b8e2d0472bd7574105f/numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f", size = 14184660, upload-time = "2025-07-24T20:28:39.522Z" }, - { url = "https://files.pythonhosted.org/packages/2b/53/102c6122db45a62aa20d1b18c9986f67e6b97e0d6fbc1ae13e3e4c84430c/numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0", size = 5113382, upload-time = "2025-07-24T20:28:48.544Z" }, - { url = "https://files.pythonhosted.org/packages/2b/21/376257efcbf63e624250717e82b4fae93d60178f09eb03ed766dbb48ec9c/numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b", size = 6647258, upload-time = "2025-07-24T20:28:59.104Z" }, - { url = "https://files.pythonhosted.org/packages/91/ba/f4ebf257f08affa464fe6036e13f2bf9d4642a40228781dc1235da81be9f/numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370", size = 14281409, upload-time = "2025-07-24T20:40:30.298Z" }, - { url = "https://files.pythonhosted.org/packages/59/ef/f96536f1df42c668cbacb727a8c6da7afc9c05ece6d558927fb1722693e1/numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73", size = 16641317, upload-time = "2025-07-24T20:40:56.625Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a7/af813a7b4f9a42f498dde8a4c6fcbff8100eed00182cc91dbaf095645f38/numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc", size = 16056262, upload-time = "2025-07-24T20:41:20.797Z" }, - { url = "https://files.pythonhosted.org/packages/8b/5d/41c4ef8404caaa7f05ed1cfb06afe16a25895260eacbd29b4d84dff2920b/numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be", size = 18579342, upload-time = "2025-07-24T20:41:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/9950e44c5a11636f4a3af6e825ec23003475cc9a466edb7a759ed3ea63bd/numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036", size = 6320610, upload-time = "2025-07-24T20:42:01.551Z" }, - { url = "https://files.pythonhosted.org/packages/7c/2f/244643a5ce54a94f0a9a2ab578189c061e4a87c002e037b0829dd77293b6/numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f", size = 12786292, upload-time = "2025-07-24T20:42:20.738Z" }, - { url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c0/c6bb172c916b00700ed3bf71cb56175fd1f7dbecebf8353545d0b5519f6c/numpy-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c8d9727f5316a256425892b043736d63e89ed15bbfe6556c5ff4d9d4448ff3b3", size = 20949074, upload-time = "2025-07-24T20:43:07.813Z" }, - { url = "https://files.pythonhosted.org/packages/20/4e/c116466d22acaf4573e58421c956c6076dc526e24a6be0903219775d862e/numpy-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:efc81393f25f14d11c9d161e46e6ee348637c0a1e8a54bf9dedc472a3fae993b", size = 14177311, upload-time = "2025-07-24T20:43:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/78/45/d4698c182895af189c463fc91d70805d455a227261d950e4e0f1310c2550/numpy-2.3.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dd937f088a2df683cbb79dda9a772b62a3e5a8a7e76690612c2737f38c6ef1b6", size = 5106022, upload-time = "2025-07-24T20:43:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/9f/76/3e6880fef4420179309dba72a8c11f6166c431cf6dee54c577af8906f914/numpy-2.3.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:11e58218c0c46c80509186e460d79fbdc9ca1eb8d8aee39d8f2dc768eb781089", size = 6640135, upload-time = "2025-07-24T20:43:49.28Z" }, - { url = "https://files.pythonhosted.org/packages/34/fa/87ff7f25b3c4ce9085a62554460b7db686fef1e0207e8977795c7b7d7ba1/numpy-2.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ad4ebcb683a1f99f4f392cc522ee20a18b2bb12a2c1c42c3d48d5a1adc9d3d2", size = 14278147, upload-time = "2025-07-24T20:44:10.328Z" }, - { url = "https://files.pythonhosted.org/packages/1d/0f/571b2c7a3833ae419fe69ff7b479a78d313581785203cc70a8db90121b9a/numpy-2.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f", size = 16635989, upload-time = "2025-07-24T20:44:34.88Z" }, - { url = "https://files.pythonhosted.org/packages/24/5a/84ae8dca9c9a4c592fe11340b36a86ffa9fd3e40513198daf8a97839345c/numpy-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66459dccc65d8ec98cc7df61307b64bf9e08101f9598755d42d8ae65d9a7a6ee", size = 16053052, upload-time = "2025-07-24T20:44:58.872Z" }, - { url = "https://files.pythonhosted.org/packages/57/7c/e5725d99a9133b9813fcf148d3f858df98511686e853169dbaf63aec6097/numpy-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7af9ed2aa9ec5950daf05bb11abc4076a108bd3c7db9aa7251d5f107079b6a6", size = 18577955, upload-time = "2025-07-24T20:45:26.714Z" }, - { url = "https://files.pythonhosted.org/packages/ae/11/7c546fcf42145f29b71e4d6f429e96d8d68e5a7ba1830b2e68d7418f0bbd/numpy-2.3.2-cp313-cp313-win32.whl", hash = "sha256:906a30249315f9c8e17b085cc5f87d3f369b35fedd0051d4a84686967bdbbd0b", size = 6311843, upload-time = "2025-07-24T20:49:24.444Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6f/a428fd1cb7ed39b4280d057720fed5121b0d7754fd2a9768640160f5517b/numpy-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:c63d95dc9d67b676e9108fe0d2182987ccb0f11933c1e8959f42fa0da8d4fa56", size = 12782876, upload-time = "2025-07-24T20:49:43.227Z" }, - { url = "https://files.pythonhosted.org/packages/65/85/4ea455c9040a12595fb6c43f2c217257c7b52dd0ba332c6a6c1d28b289fe/numpy-2.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:b05a89f2fb84d21235f93de47129dd4f11c16f64c87c33f5e284e6a3a54e43f2", size = 10192786, upload-time = "2025-07-24T20:49:59.443Z" }, - { url = "https://files.pythonhosted.org/packages/80/23/8278f40282d10c3f258ec3ff1b103d4994bcad78b0cba9208317f6bb73da/numpy-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e6ecfeddfa83b02318f4d84acf15fbdbf9ded18e46989a15a8b6995dfbf85ab", size = 21047395, upload-time = "2025-07-24T20:45:58.821Z" }, - { url = "https://files.pythonhosted.org/packages/1f/2d/624f2ce4a5df52628b4ccd16a4f9437b37c35f4f8a50d00e962aae6efd7a/numpy-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:508b0eada3eded10a3b55725b40806a4b855961040180028f52580c4729916a2", size = 14300374, upload-time = "2025-07-24T20:46:20.207Z" }, - { url = "https://files.pythonhosted.org/packages/f6/62/ff1e512cdbb829b80a6bd08318a58698867bca0ca2499d101b4af063ee97/numpy-2.3.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:754d6755d9a7588bdc6ac47dc4ee97867271b17cee39cb87aef079574366db0a", size = 5228864, upload-time = "2025-07-24T20:46:30.58Z" }, - { url = "https://files.pythonhosted.org/packages/7d/8e/74bc18078fff03192d4032cfa99d5a5ca937807136d6f5790ce07ca53515/numpy-2.3.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f66e7d2b2d7712410d3bc5684149040ef5f19856f20277cd17ea83e5006286", size = 6737533, upload-time = "2025-07-24T20:46:46.111Z" }, - { url = "https://files.pythonhosted.org/packages/19/ea/0731efe2c9073ccca5698ef6a8c3667c4cf4eea53fcdcd0b50140aba03bc/numpy-2.3.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6ea4e5a65d5a90c7d286ddff2b87f3f4ad61faa3db8dabe936b34c2275b6f8", size = 14352007, upload-time = "2025-07-24T20:47:07.1Z" }, - { url = "https://files.pythonhosted.org/packages/cf/90/36be0865f16dfed20f4bc7f75235b963d5939707d4b591f086777412ff7b/numpy-2.3.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3ef07ec8cbc8fc9e369c8dcd52019510c12da4de81367d8b20bc692aa07573a", size = 16701914, upload-time = "2025-07-24T20:47:32.459Z" }, - { url = "https://files.pythonhosted.org/packages/94/30/06cd055e24cb6c38e5989a9e747042b4e723535758e6153f11afea88c01b/numpy-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:27c9f90e7481275c7800dc9c24b7cc40ace3fdb970ae4d21eaff983a32f70c91", size = 16132708, upload-time = "2025-07-24T20:47:58.129Z" }, - { url = "https://files.pythonhosted.org/packages/9a/14/ecede608ea73e58267fd7cb78f42341b3b37ba576e778a1a06baffbe585c/numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5", size = 18651678, upload-time = "2025-07-24T20:48:25.402Z" }, - { url = "https://files.pythonhosted.org/packages/40/f3/2fe6066b8d07c3685509bc24d56386534c008b462a488b7f503ba82b8923/numpy-2.3.2-cp313-cp313t-win32.whl", hash = "sha256:c771cfac34a4f2c0de8e8c97312d07d64fd8f8ed45bc9f5726a7e947270152b5", size = 6441832, upload-time = "2025-07-24T20:48:37.181Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ba/0937d66d05204d8f28630c9c60bc3eda68824abde4cf756c4d6aad03b0c6/numpy-2.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:72dbebb2dcc8305c431b2836bcc66af967df91be793d63a24e3d9b741374c450", size = 12927049, upload-time = "2025-07-24T20:48:56.24Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ed/13542dd59c104d5e654dfa2ac282c199ba64846a74c2c4bcdbc3a0f75df1/numpy-2.3.2-cp313-cp313t-win_arm64.whl", hash = "sha256:72c6df2267e926a6d5286b0a6d556ebe49eae261062059317837fda12ddf0c1a", size = 10262935, upload-time = "2025-07-24T20:49:13.136Z" }, - { url = "https://files.pythonhosted.org/packages/c9/7c/7659048aaf498f7611b783e000c7268fcc4dcf0ce21cd10aad7b2e8f9591/numpy-2.3.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:448a66d052d0cf14ce9865d159bfc403282c9bc7bb2a31b03cc18b651eca8b1a", size = 20950906, upload-time = "2025-07-24T20:50:30.346Z" }, - { url = "https://files.pythonhosted.org/packages/80/db/984bea9d4ddf7112a04cfdfb22b1050af5757864cfffe8e09e44b7f11a10/numpy-2.3.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:546aaf78e81b4081b2eba1d105c3b34064783027a06b3ab20b6eba21fb64132b", size = 14185607, upload-time = "2025-07-24T20:50:51.923Z" }, - { url = "https://files.pythonhosted.org/packages/e4/76/b3d6f414f4eca568f469ac112a3b510938d892bc5a6c190cb883af080b77/numpy-2.3.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:87c930d52f45df092f7578889711a0768094debf73cfcde105e2d66954358125", size = 5114110, upload-time = "2025-07-24T20:51:01.041Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d2/6f5e6826abd6bca52392ed88fe44a4b52aacb60567ac3bc86c67834c3a56/numpy-2.3.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:8dc082ea901a62edb8f59713c6a7e28a85daddcb67454c839de57656478f5b19", size = 6642050, upload-time = "2025-07-24T20:51:11.64Z" }, - { url = "https://files.pythonhosted.org/packages/c4/43/f12b2ade99199e39c73ad182f103f9d9791f48d885c600c8e05927865baf/numpy-2.3.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af58de8745f7fa9ca1c0c7c943616c6fe28e75d0c81f5c295810e3c83b5be92f", size = 14296292, upload-time = "2025-07-24T20:51:33.488Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f9/77c07d94bf110a916b17210fac38680ed8734c236bfed9982fd8524a7b47/numpy-2.3.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed5527c4cf10f16c6d0b6bee1f89958bccb0ad2522c8cadc2efd318bcd545f5", size = 16638913, upload-time = "2025-07-24T20:51:58.517Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d1/9d9f2c8ea399cc05cfff8a7437453bd4e7d894373a93cdc46361bbb49a7d/numpy-2.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:095737ed986e00393ec18ec0b21b47c22889ae4b0cd2d5e88342e08b01141f58", size = 16071180, upload-time = "2025-07-24T20:52:22.827Z" }, - { url = "https://files.pythonhosted.org/packages/4c/41/82e2c68aff2a0c9bf315e47d61951099fed65d8cb2c8d9dc388cb87e947e/numpy-2.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5e40e80299607f597e1a8a247ff8d71d79c5b52baa11cc1cce30aa92d2da6e0", size = 18576809, upload-time = "2025-07-24T20:52:51.015Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/4b4fd3efb0837ed252d0f583c5c35a75121038a8c4e065f2c259be06d2d8/numpy-2.3.2-cp314-cp314-win32.whl", hash = "sha256:7d6e390423cc1f76e1b8108c9b6889d20a7a1f59d9a60cac4a050fa734d6c1e2", size = 6366410, upload-time = "2025-07-24T20:56:44.949Z" }, - { url = "https://files.pythonhosted.org/packages/11/9e/b4c24a6b8467b61aced5c8dc7dcfce23621baa2e17f661edb2444a418040/numpy-2.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:b9d0878b21e3918d76d2209c924ebb272340da1fb51abc00f986c258cd5e957b", size = 12918821, upload-time = "2025-07-24T20:57:06.479Z" }, - { url = "https://files.pythonhosted.org/packages/0e/0f/0dc44007c70b1007c1cef86b06986a3812dd7106d8f946c09cfa75782556/numpy-2.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:2738534837c6a1d0c39340a190177d7d66fdf432894f469728da901f8f6dc910", size = 10477303, upload-time = "2025-07-24T20:57:22.879Z" }, - { url = "https://files.pythonhosted.org/packages/8b/3e/075752b79140b78ddfc9c0a1634d234cfdbc6f9bbbfa6b7504e445ad7d19/numpy-2.3.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4d002ecf7c9b53240be3bb69d80f86ddbd34078bae04d87be81c1f58466f264e", size = 21047524, upload-time = "2025-07-24T20:53:22.086Z" }, - { url = "https://files.pythonhosted.org/packages/fe/6d/60e8247564a72426570d0e0ea1151b95ce5bd2f1597bb878a18d32aec855/numpy-2.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:293b2192c6bcce487dbc6326de5853787f870aeb6c43f8f9c6496db5b1781e45", size = 14300519, upload-time = "2025-07-24T20:53:44.053Z" }, - { url = "https://files.pythonhosted.org/packages/4d/73/d8326c442cd428d47a067070c3ac6cc3b651a6e53613a1668342a12d4479/numpy-2.3.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0a4f2021a6da53a0d580d6ef5db29947025ae8b35b3250141805ea9a32bbe86b", size = 5228972, upload-time = "2025-07-24T20:53:53.81Z" }, - { url = "https://files.pythonhosted.org/packages/34/2e/e71b2d6dad075271e7079db776196829019b90ce3ece5c69639e4f6fdc44/numpy-2.3.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9c144440db4bf3bb6372d2c3e49834cc0ff7bb4c24975ab33e01199e645416f2", size = 6737439, upload-time = "2025-07-24T20:54:04.742Z" }, - { url = "https://files.pythonhosted.org/packages/15/b0/d004bcd56c2c5e0500ffc65385eb6d569ffd3363cb5e593ae742749b2daa/numpy-2.3.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f92d6c2a8535dc4fe4419562294ff957f83a16ebdec66df0805e473ffaad8bd0", size = 14352479, upload-time = "2025-07-24T20:54:25.819Z" }, - { url = "https://files.pythonhosted.org/packages/11/e3/285142fcff8721e0c99b51686426165059874c150ea9ab898e12a492e291/numpy-2.3.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cefc2219baa48e468e3db7e706305fcd0c095534a192a08f31e98d83a7d45fb0", size = 16702805, upload-time = "2025-07-24T20:54:50.814Z" }, - { url = "https://files.pythonhosted.org/packages/33/c3/33b56b0e47e604af2c7cd065edca892d180f5899599b76830652875249a3/numpy-2.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76c3e9501ceb50b2ff3824c3589d5d1ab4ac857b0ee3f8f49629d0de55ecf7c2", size = 16133830, upload-time = "2025-07-24T20:55:17.306Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ae/7b1476a1f4d6a48bc669b8deb09939c56dd2a439db1ab03017844374fb67/numpy-2.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:122bf5ed9a0221b3419672493878ba4967121514b1d7d4656a7580cd11dddcbf", size = 18652665, upload-time = "2025-07-24T20:55:46.665Z" }, - { url = "https://files.pythonhosted.org/packages/14/ba/5b5c9978c4bb161034148ade2de9db44ec316fab89ce8c400db0e0c81f86/numpy-2.3.2-cp314-cp314t-win32.whl", hash = "sha256:6f1ae3dcb840edccc45af496f312528c15b1f79ac318169d094e85e4bb35fdf1", size = 6514777, upload-time = "2025-07-24T20:55:57.66Z" }, - { url = "https://files.pythonhosted.org/packages/eb/46/3dbaf0ae7c17cdc46b9f662c56da2054887b8d9e737c1476f335c83d33db/numpy-2.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b", size = 13111856, upload-time = "2025-07-24T20:56:17.318Z" }, - { url = "https://files.pythonhosted.org/packages/c1/9e/1652778bce745a67b5fe05adde60ed362d38eb17d919a540e813d30f6874/numpy-2.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:092aeb3449833ea9c0bf0089d70c29ae480685dd2377ec9cdbbb620257f84631", size = 10544226, upload-time = "2025-07-24T20:56:34.509Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ea/50ebc91d28b275b23b7128ef25c3d08152bc4068f42742867e07a870a42a/numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15", size = 21130338, upload-time = "2025-07-24T20:57:54.37Z" }, - { url = "https://files.pythonhosted.org/packages/9f/57/cdd5eac00dd5f137277355c318a955c0d8fb8aa486020c22afd305f8b88f/numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec", size = 14375776, upload-time = "2025-07-24T20:58:16.303Z" }, - { url = "https://files.pythonhosted.org/packages/83/85/27280c7f34fcd305c2209c0cdca4d70775e4859a9eaa92f850087f8dea50/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712", size = 5304882, upload-time = "2025-07-24T20:58:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/48/b4/6500b24d278e15dd796f43824e69939d00981d37d9779e32499e823aa0aa/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c", size = 6818405, upload-time = "2025-07-24T20:58:37.341Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c9/142c1e03f199d202da8e980c2496213509291b6024fd2735ad28ae7065c7/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296", size = 14419651, upload-time = "2025-07-24T20:58:59.048Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/8023e87cbea31a750a6c00ff9427d65ebc5fef104a136bfa69f76266d614/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981", size = 16760166, upload-time = "2025-07-24T21:28:56.38Z" }, - { url = "https://files.pythonhosted.org/packages/78/e3/6690b3f85a05506733c7e90b577e4762517404ea78bab2ca3a5cb1aeb78d/numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619", size = 12977811, upload-time = "2025-07-24T21:29:18.234Z" }, -] - -[[package]] -name = "orderly-set" -version = "5.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/88/39c83c35d5e97cc203e9e77a4f93bf87ec89cf6a22ac4818fdcc65d66584/orderly_set-5.5.0.tar.gz", hash = "sha256:e87185c8e4d8afa64e7f8160ee2c542a475b738bc891dc3f58102e654125e6ce", size = 27414, upload-time = "2025-07-10T20:10:55.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl", hash = "sha256:46f0b801948e98f427b412fcabb831677194c05c3b699b80de260374baa0b1e7", size = 13068, upload-time = "2025-07-10T20:10:54.377Z" }, -] - -[[package]] -name = "overture-schema" -source = { editable = "packages/overture-schema" } -dependencies = [ - { name = "overture-schema-addresses-theme" }, - { name = "overture-schema-base-theme" }, - { name = "overture-schema-buildings-theme" }, - { name = "overture-schema-core" }, - { name = "overture-schema-divisions-theme" }, - { name = "overture-schema-places-theme" }, - { name = "overture-schema-transportation-theme" }, - { name = "pydantic" }, - { name = "pyyaml" }, -] - -[package.dev-dependencies] -dev = [ - { name = "deepdiff" }, - { name = "pyyaml" }, - { name = "yamlcore" }, -] - -[package.metadata] -requires-dist = [ - { name = "overture-schema-addresses-theme", editable = "packages/overture-schema-addresses-theme" }, - { name = "overture-schema-base-theme", editable = "packages/overture-schema-base-theme" }, - { name = "overture-schema-buildings-theme", editable = "packages/overture-schema-buildings-theme" }, - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "overture-schema-divisions-theme", editable = "packages/overture-schema-divisions-theme" }, - { name = "overture-schema-places-theme", editable = "packages/overture-schema-places-theme" }, - { name = "overture-schema-transportation-theme", editable = "packages/overture-schema-transportation-theme" }, - { name = "pydantic", specifier = ">=2.0" }, - { name = "pyyaml", specifier = ">=6.0.2" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "deepdiff" }, - { name = "pyyaml" }, - { name = "yamlcore", specifier = ">=0.0.4" }, -] - -[[package]] -name = "overture-schema-addresses-theme" -source = { editable = "packages/overture-schema-addresses-theme" } -dependencies = [ - { name = "overture-schema-core" }, - { name = "pydantic" }, -] - -[package.metadata] -requires-dist = [ - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "pydantic", specifier = ">=2.0" }, -] - -[[package]] -name = "overture-schema-base-theme" -source = { editable = "packages/overture-schema-base-theme" } -dependencies = [ - { name = "overture-schema-core" }, - { name = "pydantic" }, -] - -[package.metadata] -requires-dist = [ - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "pydantic", specifier = ">=2.0" }, -] - -[[package]] -name = "overture-schema-buildings-theme" -source = { editable = "packages/overture-schema-buildings-theme" } -dependencies = [ - { name = "overture-schema-core" }, - { name = "pydantic" }, -] - -[package.metadata] -requires-dist = [ - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "pydantic", specifier = ">=2.0" }, -] - -[[package]] -name = "overture-schema-core" -source = { editable = "packages/overture-schema-core" } -dependencies = [ - { name = "pydantic" }, - { name = "shapely" }, -] - -[package.dev-dependencies] -dev = [ - { name = "jsonpath-ng" }, - { name = "pytest-subtests" }, - { name = "types-pyyaml" }, - { name = "types-shapely" }, -] - -[package.metadata] -requires-dist = [ - { name = "pydantic", specifier = ">=2.0" }, - { name = "shapely", specifier = ">=2.1.1" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "jsonpath-ng", specifier = ">=1.7.0" }, - { name = "pytest-subtests", specifier = ">=0.14.2" }, - { name = "types-pyyaml", specifier = ">=6.0.12.20250516" }, - { name = "types-shapely", specifier = ">=2.1.0.20250710" }, -] - -[[package]] -name = "overture-schema-divisions-theme" -source = { editable = "packages/overture-schema-divisions-theme" } -dependencies = [ - { name = "overture-schema-core" }, - { name = "overture-schema-validation" }, - { name = "pydantic" }, -] - -[package.metadata] -requires-dist = [ - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "overture-schema-validation", editable = "packages/overture-schema-validation" }, - { name = "pydantic", specifier = ">=2.0" }, -] - -[[package]] -name = "overture-schema-foundation" -source = { editable = "packages/overture-schema-foundation" } -dependencies = [ - { name = "pydantic" }, - { name = "shapely" }, -] - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "pydantic", specifier = ">=2.0.0" }, - { name = "shapely", specifier = ">=2.0.0" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "mypy" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "ruff" }, -] - -[[package]] -name = "overture-schema-places-theme" -source = { editable = "packages/overture-schema-places-theme" } -dependencies = [ - { name = "overture-schema-core" }, - { name = "overture-schema-validation" }, - { name = "pydantic", extra = ["email"] }, -] - -[package.metadata] -requires-dist = [ - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "overture-schema-validation", editable = "packages/overture-schema-validation" }, - { name = "pydantic", specifier = ">=2.0" }, - { name = "pydantic", extras = ["email"] }, -] - -[[package]] -name = "overture-schema-sources" -source = { editable = "packages/overture-schema-sources" } -dependencies = [ - { name = "overture-schema-core" }, - { name = "pydantic" }, -] - -[package.metadata] -requires-dist = [ - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "pydantic", specifier = ">=2.0" }, -] - -[[package]] -name = "overture-schema-transportation-theme" -source = { editable = "packages/overture-schema-transportation-theme" } -dependencies = [ - { name = "overture-schema-core" }, - { name = "pydantic" }, -] - -[package.metadata] -requires-dist = [ - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "pydantic", specifier = ">=2.0" }, -] - -[[package]] -name = "overture-schema-validation" -source = { editable = "packages/overture-schema-validation" } -dependencies = [ - { name = "pydantic" }, - { name = "shapely" }, -] - -[package.metadata] -requires-dist = [ - { name = "pydantic", specifier = ">=2.0.0" }, - { name = "shapely", specifier = ">=2.0.0" }, -] - -[[package]] -name = "overture-schema-workspace" -version = "0.0.0" -source = { virtual = "." } - -[package.dev-dependencies] -dev = [ - { name = "docformatter" }, - { name = "mypy" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "ruff" }, -] - -[package.metadata] - -[package.metadata.requires-dev] -dev = [ - { name = "docformatter", specifier = ">=1.7.7" }, - { name = "mypy" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "pytest-cov" }, - { name = "ruff" }, -] - -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "ply" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/69/882ee5c9d017149285cab114ebeab373308ef0f874fcdac9beb90e0ac4da/ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3", size = 159130, upload-time = "2018-02-15T19:01:31.097Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567, upload-time = "2018-02-15T19:01:27.172Z" }, -] - -[[package]] -name = "pydantic" -version = "2.11.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, - { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, - { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, - { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, - { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, - { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, - { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, - { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, - { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, - { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, - { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, - { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, - { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, - { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, - { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, - { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, - { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, - { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, - { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, - { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, - { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, - { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, - { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, - { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, - { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, - { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, -] - -[[package]] -name = "pygments" -version = "2.19.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, -] - -[[package]] -name = "pytest-cov" -version = "6.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage", extra = ["toml"] }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, -] - -[[package]] -name = "pytest-subtests" -version = "0.14.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/59/30/6ec8dfc678ddfd1c294212bbd7088c52d3f7fbf3f05e6d8a440c13b9741a/pytest_subtests-0.14.2.tar.gz", hash = "sha256:7154a8665fd528ee70a76d00216a44d139dc3c9c83521a0f779f7b0ad4f800de", size = 18083, upload-time = "2025-06-13T10:50:01.636Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/d4/9bf12e59fb882b0cf4f993871e1adbee094802224c429b00861acee1a169/pytest_subtests-0.14.2-py3-none-any.whl", hash = "sha256:8da0787c994ab372a13a0ad7d390533ad2e4385cac167b3ac501258c885d0b66", size = 9115, upload-time = "2025-06-13T10:50:00.543Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, -] - -[[package]] -name = "ruff" -version = "0.12.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" }, - { url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" }, - { url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" }, - { url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" }, - { url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" }, - { url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" }, - { url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" }, - { url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" }, - { url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" }, - { url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" }, - { url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" }, - { url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" }, - { url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" }, - { url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" }, -] - -[[package]] -name = "shapely" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/fa/f18025c95b86116dd8f1ec58cab078bd59ab51456b448136ca27463be533/shapely-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8ccc872a632acb7bdcb69e5e78df27213f7efd195882668ffba5405497337c6", size = 1825117, upload-time = "2025-05-19T11:03:43.547Z" }, - { url = "https://files.pythonhosted.org/packages/c7/65/46b519555ee9fb851234288be7c78be11e6260995281071d13abf2c313d0/shapely-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f24f2ecda1e6c091da64bcbef8dd121380948074875bd1b247b3d17e99407099", size = 1628541, upload-time = "2025-05-19T11:03:45.162Z" }, - { url = "https://files.pythonhosted.org/packages/29/51/0b158a261df94e33505eadfe737db9531f346dfa60850945ad25fd4162f1/shapely-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45112a5be0b745b49e50f8829ce490eb67fefb0cea8d4f8ac5764bfedaa83d2d", size = 2948453, upload-time = "2025-05-19T11:03:46.681Z" }, - { url = "https://files.pythonhosted.org/packages/a9/4f/6c9bb4bd7b1a14d7051641b9b479ad2a643d5cbc382bcf5bd52fd0896974/shapely-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c10ce6f11904d65e9bbb3e41e774903c944e20b3f0b282559885302f52f224a", size = 3057029, upload-time = "2025-05-19T11:03:48.346Z" }, - { url = "https://files.pythonhosted.org/packages/89/0b/ad1b0af491d753a83ea93138eee12a4597f763ae12727968d05934fe7c78/shapely-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:61168010dfe4e45f956ffbbaf080c88afce199ea81eb1f0ac43230065df320bd", size = 3894342, upload-time = "2025-05-19T11:03:49.602Z" }, - { url = "https://files.pythonhosted.org/packages/7d/96/73232c5de0b9fdf0ec7ddfc95c43aaf928740e87d9f168bff0e928d78c6d/shapely-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cacf067cdff741cd5c56a21c52f54ece4e4dad9d311130493a791997da4a886b", size = 4056766, upload-time = "2025-05-19T11:03:51.252Z" }, - { url = "https://files.pythonhosted.org/packages/43/cc/eec3c01f754f5b3e0c47574b198f9deb70465579ad0dad0e1cef2ce9e103/shapely-2.1.1-cp310-cp310-win32.whl", hash = "sha256:23b8772c3b815e7790fb2eab75a0b3951f435bc0fce7bb146cb064f17d35ab4f", size = 1523744, upload-time = "2025-05-19T11:03:52.624Z" }, - { url = "https://files.pythonhosted.org/packages/50/fc/a7187e6dadb10b91e66a9e715d28105cde6489e1017cce476876185a43da/shapely-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:2c7b2b6143abf4fa77851cef8ef690e03feade9a0d48acd6dc41d9e0e78d7ca6", size = 1703061, upload-time = "2025-05-19T11:03:54.695Z" }, - { url = "https://files.pythonhosted.org/packages/19/97/2df985b1e03f90c503796ad5ecd3d9ed305123b64d4ccb54616b30295b29/shapely-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587a1aa72bc858fab9b8c20427b5f6027b7cbc92743b8e2c73b9de55aa71c7a7", size = 1819368, upload-time = "2025-05-19T11:03:55.937Z" }, - { url = "https://files.pythonhosted.org/packages/56/17/504518860370f0a28908b18864f43d72f03581e2b6680540ca668f07aa42/shapely-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fa5c53b0791a4b998f9ad84aad456c988600757a96b0a05e14bba10cebaaaea", size = 1625362, upload-time = "2025-05-19T11:03:57.06Z" }, - { url = "https://files.pythonhosted.org/packages/36/a1/9677337d729b79fce1ef3296aac6b8ef4743419086f669e8a8070eff8f40/shapely-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aabecd038841ab5310d23495253f01c2a82a3aedae5ab9ca489be214aa458aa7", size = 2999005, upload-time = "2025-05-19T11:03:58.692Z" }, - { url = "https://files.pythonhosted.org/packages/a2/17/e09357274699c6e012bbb5a8ea14765a4d5860bb658df1931c9f90d53bd3/shapely-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586f6aee1edec04e16227517a866df3e9a2e43c1f635efc32978bb3dc9c63753", size = 3108489, upload-time = "2025-05-19T11:04:00.059Z" }, - { url = "https://files.pythonhosted.org/packages/17/5d/93a6c37c4b4e9955ad40834f42b17260ca74ecf36df2e81bb14d12221b90/shapely-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9878b9e37ad26c72aada8de0c9cfe418d9e2ff36992a1693b7f65a075b28647", size = 3945727, upload-time = "2025-05-19T11:04:01.786Z" }, - { url = "https://files.pythonhosted.org/packages/a3/1a/ad696648f16fd82dd6bfcca0b3b8fbafa7aacc13431c7fc4c9b49e481681/shapely-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9a531c48f289ba355e37b134e98e28c557ff13965d4653a5228d0f42a09aed0", size = 4109311, upload-time = "2025-05-19T11:04:03.134Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/150dd245beab179ec0d4472bf6799bf18f21b1efbef59ac87de3377dbf1c/shapely-2.1.1-cp311-cp311-win32.whl", hash = "sha256:4866de2673a971820c75c0167b1f1cd8fb76f2d641101c23d3ca021ad0449bab", size = 1522982, upload-time = "2025-05-19T11:04:05.217Z" }, - { url = "https://files.pythonhosted.org/packages/93/5b/842022c00fbb051083c1c85430f3bb55565b7fd2d775f4f398c0ba8052ce/shapely-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:20a9d79958b3d6c70d8a886b250047ea32ff40489d7abb47d01498c704557a93", size = 1703872, upload-time = "2025-05-19T11:04:06.791Z" }, - { url = "https://files.pythonhosted.org/packages/fb/64/9544dc07dfe80a2d489060791300827c941c451e2910f7364b19607ea352/shapely-2.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2827365b58bf98efb60affc94a8e01c56dd1995a80aabe4b701465d86dcbba43", size = 1833021, upload-time = "2025-05-19T11:04:08.022Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/fb5f545e72e89b6a0f04a0effda144f5be956c9c312c7d4e00dfddbddbcf/shapely-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c551f7fa7f1e917af2347fe983f21f212863f1d04f08eece01e9c275903fad", size = 1643018, upload-time = "2025-05-19T11:04:09.343Z" }, - { url = "https://files.pythonhosted.org/packages/03/46/61e03edba81de729f09d880ce7ae5c1af873a0814206bbfb4402ab5c3388/shapely-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78dec4d4fbe7b1db8dc36de3031767e7ece5911fb7782bc9e95c5cdec58fb1e9", size = 2986417, upload-time = "2025-05-19T11:04:10.56Z" }, - { url = "https://files.pythonhosted.org/packages/1f/1e/83ec268ab8254a446b4178b45616ab5822d7b9d2b7eb6e27cf0b82f45601/shapely-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:872d3c0a7b8b37da0e23d80496ec5973c4692920b90de9f502b5beb994bbaaef", size = 3098224, upload-time = "2025-05-19T11:04:11.903Z" }, - { url = "https://files.pythonhosted.org/packages/f1/44/0c21e7717c243e067c9ef8fa9126de24239f8345a5bba9280f7bb9935959/shapely-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2e2b9125ebfbc28ecf5353511de62f75a8515ae9470521c9a693e4bb9fbe0cf1", size = 3925982, upload-time = "2025-05-19T11:04:13.224Z" }, - { url = "https://files.pythonhosted.org/packages/15/50/d3b4e15fefc103a0eb13d83bad5f65cd6e07a5d8b2ae920e767932a247d1/shapely-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4b96cea171b3d7f6786976a0520f178c42792897653ecca0c5422fb1e6946e6d", size = 4089122, upload-time = "2025-05-19T11:04:14.477Z" }, - { url = "https://files.pythonhosted.org/packages/bd/05/9a68f27fc6110baeedeeebc14fd86e73fa38738c5b741302408fb6355577/shapely-2.1.1-cp312-cp312-win32.whl", hash = "sha256:39dca52201e02996df02e447f729da97cfb6ff41a03cb50f5547f19d02905af8", size = 1522437, upload-time = "2025-05-19T11:04:16.203Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e9/a4560e12b9338842a1f82c9016d2543eaa084fce30a1ca11991143086b57/shapely-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:13d643256f81d55a50013eff6321142781cf777eb6a9e207c2c9e6315ba6044a", size = 1703479, upload-time = "2025-05-19T11:04:18.497Z" }, - { url = "https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3004a644d9e89e26c20286d5fdc10f41b1744c48ce910bd1867fdff963fe6c48", size = 1832107, upload-time = "2025-05-19T11:04:19.736Z" }, - { url = "https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1415146fa12d80a47d13cfad5310b3c8b9c2aa8c14a0c845c9d3d75e77cb54f6", size = 1642355, upload-time = "2025-05-19T11:04:21.035Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/6d28b43d53fea56de69c744e34c2b999ed4042f7a811dc1bceb876071c95/shapely-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21fcab88b7520820ec16d09d6bea68652ca13993c84dffc6129dc3607c95594c", size = 2968871, upload-time = "2025-05-19T11:04:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ce6a5cc52c974b291237a96c08c5592e50f066871704fb5b12be2639d9026a", size = 3080830, upload-time = "2025-05-19T11:04:23.997Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fe/f4a03d81abd96a6ce31c49cd8aaba970eaaa98e191bd1e4d43041e57ae5a/shapely-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:04e4c12a45a1d70aeb266618d8cf81a2de9c4df511b63e105b90bfdfb52146de", size = 3908961, upload-time = "2025-05-19T11:04:25.702Z" }, - { url = "https://files.pythonhosted.org/packages/ef/59/7605289a95a6844056a2017ab36d9b0cb9d6a3c3b5317c1f968c193031c9/shapely-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ca74d851ca5264aae16c2b47e96735579686cb69fa93c4078070a0ec845b8d8", size = 4079623, upload-time = "2025-05-19T11:04:27.171Z" }, - { url = "https://files.pythonhosted.org/packages/bc/4d/9fea036eff2ef4059d30247128b2d67aaa5f0b25e9fc27e1d15cc1b84704/shapely-2.1.1-cp313-cp313-win32.whl", hash = "sha256:fd9130501bf42ffb7e0695b9ea17a27ae8ce68d50b56b6941c7f9b3d3453bc52", size = 1521916, upload-time = "2025-05-19T11:04:28.405Z" }, - { url = "https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:ab8d878687b438a2f4c138ed1a80941c6ab0029e0f4c785ecfe114413b498a97", size = 1702746, upload-time = "2025-05-19T11:04:29.643Z" }, - { url = "https://files.pythonhosted.org/packages/60/36/b1452e3e7f35f5f6454d96f3be6e2bb87082720ff6c9437ecc215fa79be0/shapely-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c062384316a47f776305ed2fa22182717508ffdeb4a56d0ff4087a77b2a0f6d", size = 1833482, upload-time = "2025-05-19T11:04:30.852Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ca/8e6f59be0718893eb3e478141285796a923636dc8f086f83e5b0ec0036d0/shapely-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4ecf6c196b896e8f1360cc219ed4eee1c1e5f5883e505d449f263bd053fb8c05", size = 1642256, upload-time = "2025-05-19T11:04:32.068Z" }, - { url = "https://files.pythonhosted.org/packages/ab/78/0053aea449bb1d4503999525fec6232f049abcdc8df60d290416110de943/shapely-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb00070b4c4860f6743c600285109c273cca5241e970ad56bb87bef0be1ea3a0", size = 3016614, upload-time = "2025-05-19T11:04:33.7Z" }, - { url = "https://files.pythonhosted.org/packages/ee/53/36f1b1de1dfafd1b457dcbafa785b298ce1b8a3e7026b79619e708a245d5/shapely-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14a9afa5fa980fbe7bf63706fdfb8ff588f638f145a1d9dbc18374b5b7de913", size = 3093542, upload-time = "2025-05-19T11:04:34.952Z" }, - { url = "https://files.pythonhosted.org/packages/b9/bf/0619f37ceec6b924d84427c88835b61f27f43560239936ff88915c37da19/shapely-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b640e390dabde790e3fb947198b466e63223e0a9ccd787da5f07bcb14756c28d", size = 3945961, upload-time = "2025-05-19T11:04:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/93/c9/20ca4afeb572763b07a7997f00854cb9499df6af85929e93012b189d8917/shapely-2.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:69e08bf9697c1b73ec6aa70437db922bafcea7baca131c90c26d59491a9760f9", size = 4089514, upload-time = "2025-05-19T11:04:37.683Z" }, - { url = "https://files.pythonhosted.org/packages/33/6a/27036a5a560b80012a544366bceafd491e8abb94a8db14047b5346b5a749/shapely-2.1.1-cp313-cp313t-win32.whl", hash = "sha256:ef2d09d5a964cc90c2c18b03566cf918a61c248596998a0301d5b632beadb9db", size = 1540607, upload-time = "2025-05-19T11:04:38.925Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f1/5e9b3ba5c7aa7ebfaf269657e728067d16a7c99401c7973ddf5f0cf121bd/shapely-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8cb8f17c377260452e9d7720eeaf59082c5f8ea48cf104524d953e5d36d4bdb7", size = 1723061, upload-time = "2025-05-19T11:04:40.082Z" }, -] - -[[package]] -name = "tomli" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, -] - -[[package]] -name = "types-pyyaml" -version = "6.0.12.20250516" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378, upload-time = "2025-05-16T03:08:04.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312, upload-time = "2025-05-16T03:08:04.019Z" }, -] - -[[package]] -name = "types-shapely" -version = "2.1.0.20250710" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/70/37f63bc62aefde18795bd807381f7e2c2b7884f7f1ddaed2e6f727a099f9/types_shapely-2.1.0.20250710.tar.gz", hash = "sha256:f93067996f5203dffe57f57a130ae757b7e83609b046ea9631de8b900e4be77b", size = 26205, upload-time = "2025-07-10T03:15:50.902Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/f1/bc36b6a7dbe742b0dc75c0fe6b4f27a2a37dedcf63bac9afe55bdb998870/types_shapely-2.1.0.20250710-py3-none-any.whl", hash = "sha256:cf58a2f6a79ec7cbdaf2ba8a8d744842bd1f5ae159afc2512db552b2c67219f4", size = 37696, upload-time = "2025-07-10T03:15:49.852Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, -] - -[[package]] -name = "untokenize" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2", size = 3099, upload-time = "2014-02-08T16:30:40.631Z" } - -[[package]] -name = "yamlcore" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/d4/113dfc4c85f945de13b045a20eca50be5f6efa33342d9a9bd6162d26b288/yamlcore-0.0.4.tar.gz", hash = "sha256:c2b1a08dd117cf76a70ee124e936b3c568b0b5c4e5131e69b513f4a877a44881", size = 10456, upload-time = "2024-10-09T17:26:02.601Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/47/632498fd9a3fe4f175e5de71162dd8cd9b2141180a98f0bef6af293aeaa6/yamlcore-0.0.4-py3-none-any.whl", hash = "sha256:f82a7f7d16baf6531a8c3266b0b63ece251ee2abaf2b9eee040a2f64c8fc618d", size = 5969, upload-time = "2024-10-09T17:26:01.507Z" }, -] From c3dbf6f8eb9c6181ea533f33f3b148b19be5fd24 Mon Sep 17 00:00:00 2001 From: schapper Date: Tue, 30 Sep 2025 17:33:31 -0700 Subject: [PATCH 03/20] wip - move bbox --- .../overture-schema-core/src/overture/schema/core/models.py | 2 +- packages/overture-schema-core/tests/test_models.py | 2 +- .../src/overture/schema/foundation/primitive}/bbox.py | 0 .../tests/primitive}/test_bbox.py | 3 ++- 4 files changed, 4 insertions(+), 3 deletions(-) rename packages/{overture-schema-core/src/overture/schema/core => overture-schema-foundation/src/overture/schema/foundation/primitive}/bbox.py (100%) rename packages/{overture-schema-core/tests => overture-schema-foundation/tests/primitive}/test_bbox.py (98%) diff --git a/packages/overture-schema-core/src/overture/schema/core/models.py b/packages/overture-schema-core/src/overture/schema/core/models.py index e67cb194a..b53c5f5c1 100644 --- a/packages/overture-schema-core/src/overture/schema/core/models.py +++ b/packages/overture-schema-core/src/overture/schema/core/models.py @@ -12,7 +12,7 @@ ) from pydantic_core import core_schema -from overture.schema.core.bbox import BBox +from overture.schema.foundation.primitive.bbox import BBox from overture.schema.foundation.primitive.geometry import Geometry from overture.schema.validation import ( allow_extension_fields, diff --git a/packages/overture-schema-core/tests/test_models.py b/packages/overture-schema-core/tests/test_models.py index a56730805..882e02bfd 100644 --- a/packages/overture-schema-core/tests/test_models.py +++ b/packages/overture-schema-core/tests/test_models.py @@ -3,9 +3,9 @@ import pytest from deepdiff import DeepDiff -from overture.schema.core.bbox import BBox from overture.schema.core.json_schema import EnhancedJsonSchemaGenerator from overture.schema.core.models import Feature +from overture.schema.foundation.primitive.bbox import BBox from overture.schema.foundation.primitive.geometry import Geometry from shapely.geometry import LineString, Point diff --git a/packages/overture-schema-core/src/overture/schema/core/bbox.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/bbox.py similarity index 100% rename from packages/overture-schema-core/src/overture/schema/core/bbox.py rename to packages/overture-schema-foundation/src/overture/schema/foundation/primitive/bbox.py diff --git a/packages/overture-schema-core/tests/test_bbox.py b/packages/overture-schema-foundation/tests/primitive/test_bbox.py similarity index 98% rename from packages/overture-schema-core/tests/test_bbox.py rename to packages/overture-schema-foundation/tests/primitive/test_bbox.py index 713770dd3..68a6f3e36 100644 --- a/packages/overture-schema-core/tests/test_bbox.py +++ b/packages/overture-schema-foundation/tests/primitive/test_bbox.py @@ -2,9 +2,10 @@ import jsonpath_ng # type: ignore[import-untyped] import pytest -from overture.schema.core.bbox import BBox from pydantic import BaseModel, ValidationError +from overture.schema.foundation.primitive.bbox import BBox + @pytest.mark.parametrize( "xmin, ymin, xmax, ymax", From 0e2d001834bc10641d0e883361e0b964987d8d28 Mon Sep 17 00:00:00 2001 From: schapper Date: Wed, 1 Oct 2025 10:39:36 -0700 Subject: [PATCH 04/20] wip - move primitive numeric types --- .../schema/addresses/address/models.py | 2 +- .../overture/schema/base/bathymetry/models.py | 2 +- .../schema/base/infrastructure/models.py | 2 +- .../src/overture/schema/base/land/models.py | 2 +- .../overture/schema/base/land_cover/models.py | 2 +- .../overture/schema/base/land_use/models.py | 2 +- .../src/overture/schema/base/types.py | 2 +- .../src/overture/schema/base/water/models.py | 2 +- .../schema/buildings/building/models.py | 2 +- .../schema/buildings/building_part/models.py | 2 +- .../src/overture/schema/buildings/models.py | 2 +- .../src/overture/schema/core/models.py | 6 +- .../schema/core/primitives/__init__.py | 28 --------- .../src/overture/schema/core/types.py | 3 +- .../test_json_schema_for_primitive_types.py | 2 +- .../overture-schema-core/tests/test_models.py | 6 +- .../tests/test_primitive_types.py | 2 +- .../schema/divisions/division/models.py | 4 +- .../schema/divisions/division_area/models.py | 2 +- .../divisions/division_boundary/models.py | 2 +- .../schema/foundation/primitive/__init__.py | 57 +++++++++++++++++++ .../primitive/{geometry.py => geom.py} | 0 .../schema/foundation/primitive/num.py} | 0 .../tests/primitive/test_geometry.py | 2 +- .../overture/schema/places/place/models.py | 2 +- .../schema/transportation/connector/models.py | 2 +- .../overture/schema/transportation/models.py | 2 +- .../schema/transportation/segment/models.py | 2 +- .../tests/test_hashability.py | 2 +- .../src/overture/schema/validation/types.py | 2 +- 30 files changed, 90 insertions(+), 58 deletions(-) create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py rename packages/overture-schema-foundation/src/overture/schema/foundation/primitive/{geometry.py => geom.py} (100%) rename packages/{overture-schema-core/src/overture/schema/core/primitives/numeric.py => overture-schema-foundation/src/overture/schema/foundation/primitive/num.py} (100%) diff --git a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py index 209a06ae4..7ba553a53 100644 --- a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py +++ b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py @@ -9,7 +9,7 @@ StrictBaseModel, ) from overture.schema.core.types import CountryCode, TrimmedString -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py index ecb8242db..424100580 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py @@ -9,7 +9,7 @@ Feature, ) from overture.schema.core.models import CartographicallyHinted -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py index ff87fe920..aac7ef90d 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py @@ -14,7 +14,7 @@ Feature, ) from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py index 27b3fad4a..b27df789e 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py @@ -11,7 +11,7 @@ Feature, ) from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py index a5e574503..8aae24cb3 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py @@ -9,7 +9,7 @@ Feature, ) from overture.schema.core.models import CartographicallyHinted -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py index 228750b91..9f32326e4 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py @@ -11,7 +11,7 @@ Feature, ) from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/types.py b/packages/overture-schema-base-theme/src/overture/schema/base/types.py index 47b233e4e..1698be4f1 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/types.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/types.py @@ -2,7 +2,7 @@ from pydantic import Field -from overture.schema.core.primitives import float64, int32 +from overture.schema.foundation.primitive import float64, int32 Elevation = NewType( "Elevation", diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py index ddedbf6d5..ba18b0344 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py @@ -10,7 +10,7 @@ Feature, ) from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py index 3cfd45c40..c392cd7e5 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py @@ -6,7 +6,7 @@ from overture.schema.core import Feature from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py index ba75e6981..8f25037b1 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py @@ -8,7 +8,7 @@ from overture.schema.core.models import Named, Stacked from overture.schema.core.ref import Reference, Relationship from overture.schema.core.types import Id -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py index 383d518e9..6950f82f8 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py @@ -8,8 +8,8 @@ RoofOrientation, RoofShape, ) -from overture.schema.core.primitives import float64, int32 from overture.schema.core.types import HexColor +from overture.schema.foundation.primitive import float64, int32 class Shape(BaseModel): diff --git a/packages/overture-schema-core/src/overture/schema/core/models.py b/packages/overture-schema-core/src/overture/schema/core/models.py index b53c5f5c1..e8a956a0e 100644 --- a/packages/overture-schema-core/src/overture/schema/core/models.py +++ b/packages/overture-schema-core/src/overture/schema/core/models.py @@ -12,8 +12,10 @@ ) from pydantic_core import core_schema -from overture.schema.foundation.primitive.bbox import BBox -from overture.schema.foundation.primitive.geometry import Geometry +from overture.schema.foundation.primitive import ( + BBox, + Geometry, +) from overture.schema.validation import ( allow_extension_fields, ) diff --git a/packages/overture-schema-core/src/overture/schema/core/primitives/__init__.py b/packages/overture-schema-core/src/overture/schema/core/primitives/__init__.py index e2c4cbd5f..e69de29bb 100644 --- a/packages/overture-schema-core/src/overture/schema/core/primitives/__init__.py +++ b/packages/overture-schema-core/src/overture/schema/core/primitives/__init__.py @@ -1,28 +0,0 @@ -"""Primitive data types. - -This module provides additional, more specific primitive types that come with automatic -constraint validation and are intended to support multi-target serialization support -(where the list of and names for these types vary). -""" - -from .numeric import ( - float32, - float64, - int8, - int32, - int64, - uint8, - uint16, - uint32, -) - -__all__ = [ - "uint8", - "uint16", - "uint32", - "int8", - "int32", - "int64", - "float32", - "float64", -] diff --git a/packages/overture-schema-core/src/overture/schema/core/types.py b/packages/overture-schema-core/src/overture/schema/core/types.py index 5aaf794b6..7b1a36fae 100644 --- a/packages/overture-schema-core/src/overture/schema/core/types.py +++ b/packages/overture-schema-core/src/overture/schema/core/types.py @@ -3,6 +3,7 @@ from pydantic import Field +from overture.schema.foundation.primitive import float64, int32 from overture.schema.validation.constraints import ( CountryCodeConstraint, LinearReferenceRangeConstraint, @@ -19,8 +20,6 @@ WikidataId, ) -from .primitives.numeric import float64, int32 - Id = NewType( "Id", Annotated[ diff --git a/packages/overture-schema-core/tests/test_json_schema_for_primitive_types.py b/packages/overture-schema-core/tests/test_json_schema_for_primitive_types.py index 26ba5e742..51c535eaa 100644 --- a/packages/overture-schema-core/tests/test_json_schema_for_primitive_types.py +++ b/packages/overture-schema-core/tests/test_json_schema_for_primitive_types.py @@ -1,7 +1,7 @@ """Tests for JSON Schema generation with primitive types.""" from overture.schema.core.json_schema import json_schema -from overture.schema.core.primitives import ( +from overture.schema.foundation.primitive import ( float32, float64, int32, diff --git a/packages/overture-schema-core/tests/test_models.py b/packages/overture-schema-core/tests/test_models.py index 882e02bfd..3bda2ce9e 100644 --- a/packages/overture-schema-core/tests/test_models.py +++ b/packages/overture-schema-core/tests/test_models.py @@ -5,8 +5,10 @@ from deepdiff import DeepDiff from overture.schema.core.json_schema import EnhancedJsonSchemaGenerator from overture.schema.core.models import Feature -from overture.schema.foundation.primitive.bbox import BBox -from overture.schema.foundation.primitive.geometry import Geometry +from overture.schema.foundation.primitive import ( + BBox, + Geometry, +) from shapely.geometry import LineString, Point diff --git a/packages/overture-schema-core/tests/test_primitive_types.py b/packages/overture-schema-core/tests/test_primitive_types.py index c559b2b51..5c3a04d1c 100644 --- a/packages/overture-schema-core/tests/test_primitive_types.py +++ b/packages/overture-schema-core/tests/test_primitive_types.py @@ -3,7 +3,7 @@ from typing import Annotated import pytest -from overture.schema.core.primitives import ( +from overture.schema.foundation.primitive import ( float32, float64, int32, diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py index 13b7907f2..9d76ba955 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py @@ -15,7 +15,6 @@ Perspectives, StrictBaseModel, ) -from overture.schema.core.primitives import int32 from overture.schema.core.types import ( CommonNames, CountryCode, @@ -26,10 +25,11 @@ from overture.schema.core.validation import ( UniqueItemsConstraint, ) -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, + int32, ) from ..enums import DivisionClass, PlaceType diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py index 9d7523a15..ab0aa96ec 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py @@ -16,7 +16,7 @@ from overture.schema.core.validation import ( exactly_one_of, ) -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py index c2eb9b2e8..dfae55d37 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py @@ -19,7 +19,7 @@ exactly_one_of, not_required_if, ) -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py new file mode 100644 index 000000000..84f09fc4a --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py @@ -0,0 +1,57 @@ +""" +primitive +========= +Primitive data types. + +This module provides a set of primitive types that can be used as fields in Pydantic models but are +not themselves models. The primitive types include specific numeric types such as int32 and +geometric types including `Geometry` and `BBox`. + +Primitives are intended to provide specific, well-defined behavior for a wide range of serialization +targets including not just Pydantic models and JSON, but also a range of other serialization +targets, for example, the Parquet format or Spark dataframes. They come with built-in Pydantic +constraints, but also specific documented behavior expectations so they can be supported by other +serialization targets. + +Modules +------- +bbox : module + Bounding box type. +geom : module + Geometry type. +num : module + Numeric primitives such as int32 and float64. + +""" + +from .bbox import BBox +from .geom import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) +from .num import ( + float32, + float64, + int8, + int32, + int64, + uint8, + uint16, + uint32, +) + +__all__ = [ + "BBox", + "Geometry", + "GeometryType", + "GeometryTypeConstraint", + "uint8", + "uint16", + "uint32", + "int8", + "int32", + "int64", + "float32", + "float64", +] diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geometry.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geom.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geometry.py rename to packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geom.py diff --git a/packages/overture-schema-core/src/overture/schema/core/primitives/numeric.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py similarity index 100% rename from packages/overture-schema-core/src/overture/schema/core/primitives/numeric.py rename to packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py diff --git a/packages/overture-schema-foundation/tests/primitive/test_geometry.py b/packages/overture-schema-foundation/tests/primitive/test_geometry.py index 84ae51b63..10c614cfc 100644 --- a/packages/overture-schema-foundation/tests/primitive/test_geometry.py +++ b/packages/overture-schema-foundation/tests/primitive/test_geometry.py @@ -9,7 +9,7 @@ from pytest_subtests import SubTests from shapely import wkt -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py index 6a4184912..2bf713495 100644 --- a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py +++ b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py @@ -20,7 +20,7 @@ from overture.schema.core.validation import ( UniqueItemsConstraint, ) -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py index 6db452944..97c5a745d 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py @@ -7,7 +7,7 @@ from overture.schema.core import ( Feature, ) -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py index a5bb92874..12e3dd4a3 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py @@ -9,7 +9,6 @@ StrictBaseModel, ) from overture.schema.core.models import GeometricRangeScope -from overture.schema.core.primitives import float64, int32 from overture.schema.core.ref import Reference, Relationship from overture.schema.core.types import ( Id, @@ -25,6 +24,7 @@ any_of, min_properties, ) +from overture.schema.foundation.primitive import float64, int32 from .enums import ( AccessType, diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py index b7e3afd9e..97df57b6c 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py @@ -11,7 +11,7 @@ Named, ) from overture.schema.core.validation import UniqueItemsConstraint -from overture.schema.foundation.primitive.geometry import ( +from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-transportation-theme/tests/test_hashability.py b/packages/overture-schema-transportation-theme/tests/test_hashability.py index 68259d97f..e3005fc6a 100644 --- a/packages/overture-schema-transportation-theme/tests/test_hashability.py +++ b/packages/overture-schema-transportation-theme/tests/test_hashability.py @@ -1,7 +1,7 @@ """Tests for hashability of all scope and related classes.""" from overture.schema.core.models import GeometricRangeScope -from overture.schema.foundation.primitive.geometry import Geometry +from overture.schema.foundation.primitive import Geometry from overture.schema.transportation.enums import ( AccessType, DestinationLabelType, diff --git a/packages/overture-schema-validation/src/overture/schema/validation/types.py b/packages/overture-schema-validation/src/overture/schema/validation/types.py index 8a1e2c193..e29191895 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/types.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/types.py @@ -4,7 +4,7 @@ from pydantic import Field -from overture.schema.core.primitives import float64 +from overture.schema.foundation.primitive import float64 from .constraints import ( CategoryPatternConstraint, From 16a1f4bb430c463370adaae1554ce5b549ab4115 Mon Sep 17 00:00:00 2001 From: schapper Date: Wed, 1 Oct 2025 10:56:08 -0700 Subject: [PATCH 05/20] wip - Document numeric primitives and add missing `int16` --- .../schema/foundation/primitive/__init__.py | 10 ++- .../schema/foundation/primitive/num.py | 81 +++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py index 84f09fc4a..b3b8fa6a7 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py @@ -34,8 +34,10 @@ float32, float64, int8, + int16, int32, int64, + pct, uint8, uint16, uint32, @@ -46,12 +48,14 @@ "Geometry", "GeometryType", "GeometryTypeConstraint", - "uint8", - "uint16", - "uint32", "int8", + "int16", "int32", "int64", "float32", "float64", + "pct", + "uint8", + "uint16", + "uint32", ] diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py index 91d8d4c39..7d183faa7 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py @@ -3,10 +3,91 @@ from pydantic import Field uint8 = NewType("uint8", Annotated[int, Field(ge=0, le=255)]) # type: ignore [type-arg] +uint8.__doc__ = """ +uint8 : NewType + Portable 8-bit unsigned integer. + + This is an int at runtime, but using uint8 for fields instead of int makes them portable across + different serialization and validation platforms. +""" + uint16 = NewType("uint16", Annotated[int, Field(ge=0, le=65535)]) # type: ignore[type-arg] +uint16.__doc__ = """ +uint16 : NewType + Portable 16-bit unsigned integer. + + This is an int at runtime, but using uint16 for fields instead of int makes them portable across + different serialization and validation platforms. +""" + uint32 = NewType("uint32", Annotated[int, Field(ge=0, le=4294967295)]) # type: ignore[type-arg] +uint32.__doc__ = """ +uint32 : NewType + Portable 32-bit unsigned integer. + + This is an int at runtime, but using uint32 for fields instead of int makes them portable across + different serialization and validation platforms. +""" + int8 = NewType("int8", Annotated[int, Field(ge=-128, le=127)]) # type: ignore[type-arg] +int8.__doc__ = """ +int8 : NewType + Portable 8-bit signed integer. + + This is an int at runtime, but using int8 for fields instead of int makes them portable across + different serialization and validation platforms. +""" + +int16 = NewType("int16", Annotated[int, Field(ge=-32768, le=32767)]) # type: ignore[type-arg] +int16.__doc__ = """ +int16 : NewType + Portable 16-bit signed integer. + + This is an int at runtime, but using int16 for fields instead of int makes them portable across + different serialization and validation platforms. +""" + int32 = NewType("int32", Annotated[int, Field(ge=-(2**31), le=2**31 - 1)]) # type: ignore[type-arg] +int32.__doc__ = """ +int32 : NewType + Portable 32-bit signed integer. + + This is an int at runtime, but using int32 for fields instead of int makes them portable across + different serialization and validation platforms. +""" + int64 = NewType("int64", Annotated[int, Field(ge=-(2**63), le=2**63 - 1)]) # type: ignore[type-arg] +int64.__doc__ = """ +int64 : NewType + Portable 64-bit signed integer. + + This is an int at runtime, but using int64 for fields instead of int makes them portable across + different serialization and validation platforms. +""" + float32 = NewType("float32", float) +float32.__doc__ = """ +float32 : NewType + Portable IEEE 32-bit floating point number. + + This is a float at runtime, but using float32 for fields instead of float makes them portable + across different serialization and validation platforms. +""" + float64 = NewType("float64", float) +float64.__doc__ = """ +float64 : NewType + Portable IEEE 64-bit floating point number. + + This is a float at runtime, but using float64 for fields instead of float makes them portable + across different serialization and validation platforms. +""" + +pct = NewType("pct", Annotated[float, Field(ge=0, le=1)]) +pct.__doc__ = """ +pct : NewType + Portable percent value in the range [0, 1] where 0 represents 0% and 1 represents 100%. + + This is a float at runtime, but using pct for fields instead of float makes them portable + across different serialization and validation platforms. +""" From 41ae7aa90302955df92b69acf918824ca25a2dc7 Mon Sep 17 00:00:00 2001 From: schapper Date: Wed, 1 Oct 2025 11:02:26 -0700 Subject: [PATCH 06/20] wip - replace LR base types with pct, but not done for Confidence yet --- .../overture-schema-core/src/overture/schema/core/types.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/overture-schema-core/src/overture/schema/core/types.py b/packages/overture-schema-core/src/overture/schema/core/types.py index 7b1a36fae..f30cea227 100644 --- a/packages/overture-schema-core/src/overture/schema/core/types.py +++ b/packages/overture-schema-core/src/overture/schema/core/types.py @@ -3,7 +3,7 @@ from pydantic import Field -from overture.schema.foundation.primitive import float64, int32 +from overture.schema.foundation.primitive import float64, int32, pct from overture.schema.validation.constraints import ( CountryCodeConstraint, LinearReferenceRangeConstraint, @@ -37,10 +37,8 @@ LinearlyReferencedPosition = NewType( "LinearlyReferencedPosition", Annotated[ - float64, + pct, Field( - ge=0, - le=1, description="Represents a linearly-referenced position between 0% and 100% of the distance along a path such as a road segment or a river center-line segment.", ), ], From d2c4b5f56630e3bdc6c43f7a7ae29716d0f70b3e Mon Sep 17 00:00:00 2001 From: schapper Date: Wed, 1 Oct 2025 12:08:10 -0700 Subject: [PATCH 07/20] wip - Migrate basic constraint, collection constraints --- .../src/overture/schema/core/models.py | 3 +- .../src/overture/schema/core/types.py | 2 +- .../src/overture/schema/core/validation.py | 2 +- .../schema/divisions/division/models.py | 2 +- .../divisions/division_boundary/models.py | 2 +- .../src/overture/schema/divisions/types.py | 2 +- .../schema/foundation/constraint/__init__.py | 28 +++++ .../foundation/constraint/collection.py | 97 ++++++++++++++++ .../foundation/constraint/constraint.py | 29 +++++ .../overture/schema/places/place/models.py | 2 +- .../overture/schema/transportation/models.py | 2 +- .../schema/transportation/segment/models.py | 2 +- .../overture/schema/transportation/types.py | 2 +- .../overture/schema/validation/__init__.py | 3 +- .../overture/schema/validation/constraints.py | 106 +----------------- .../tests/test_constraints.py | 2 +- .../tests/test_json_schema_generation.py | 2 +- 17 files changed, 175 insertions(+), 113 deletions(-) create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/constraint/collection.py create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/constraint/constraint.py diff --git a/packages/overture-schema-core/src/overture/schema/core/models.py b/packages/overture-schema-core/src/overture/schema/core/models.py index e8a956a0e..cdaae44e5 100644 --- a/packages/overture-schema-core/src/overture/schema/core/models.py +++ b/packages/overture-schema-core/src/overture/schema/core/models.py @@ -12,6 +12,7 @@ ) from pydantic_core import core_schema +from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.foundation.primitive import ( BBox, Geometry, @@ -39,7 +40,7 @@ SortKey, TrimmedString, ) -from .validation import ConstraintValidatedModel, UniqueItemsConstraint +from .validation import ConstraintValidatedModel class StrictBaseModel(BaseModel): diff --git a/packages/overture-schema-core/src/overture/schema/core/types.py b/packages/overture-schema-core/src/overture/schema/core/types.py index f30cea227..880eaa759 100644 --- a/packages/overture-schema-core/src/overture/schema/core/types.py +++ b/packages/overture-schema-core/src/overture/schema/core/types.py @@ -3,7 +3,7 @@ from pydantic import Field -from overture.schema.foundation.primitive import float64, int32, pct +from overture.schema.foundation.primitive import int32, pct from overture.schema.validation.constraints import ( CountryCodeConstraint, LinearReferenceRangeConstraint, diff --git a/packages/overture-schema-core/src/overture/schema/core/validation.py b/packages/overture-schema-core/src/overture/schema/core/validation.py index fbdc54e60..aaa254498 100644 --- a/packages/overture-schema-core/src/overture/schema/core/validation.py +++ b/packages/overture-schema-core/src/overture/schema/core/validation.py @@ -1,6 +1,6 @@ +from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.validation import ( ConstraintValidatedModel, - UniqueItemsConstraint, any_of, exactly_one_of, min_properties, diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py index 9d76ba955..585c254d3 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py @@ -22,7 +22,7 @@ RegionCode, WikidataId, ) -from overture.schema.core.validation import ( +from overture.schema.foundation.constraint import ( UniqueItemsConstraint, ) from overture.schema.foundation.primitive import ( diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py index dfae55d37..a58e4946a 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py @@ -15,10 +15,10 @@ RegionCode, ) from overture.schema.core.validation import ( - UniqueItemsConstraint, exactly_one_of, not_required_if, ) +from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.foundation.primitive import ( Geometry, GeometryType, diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/types.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/types.py index 04bb75453..7bc79cff5 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/types.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/types.py @@ -2,7 +2,7 @@ from pydantic import Field -from overture.schema.core.validation import UniqueItemsConstraint +from overture.schema.foundation.constraint import UniqueItemsConstraint from .models import HierarchyItem diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py new file mode 100644 index 000000000..8f2e9187c --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py @@ -0,0 +1,28 @@ +""" +constraint +========== +Reusable constraints. + +todo - vic + + +Modules +------- +constraint : module + Base constraint type. +collection : module + Constraints for collections + +""" + +from .collection import ( + CollectionConstraint, + UniqueItemsConstraint, +) +from .constraint import Constraint + +__all__ = [ + "Constraint", + "CollectionConstraint", + "UniqueItemsConstraint", +] diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/collection.py b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/collection.py new file mode 100644 index 000000000..0396e702d --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/collection.py @@ -0,0 +1,97 @@ +""" +collection +========== +Constraints for collection types. + +This module provides a set of reusable Pydantic constraints that can be applied to collections such +as lists and dicts to further constrain them. + + +Types +----- + +""" + +from collections.abc import Collection +from typing import Any, get_origin + +from pydantic import ( + GetCoreSchemaHandler, + GetJsonSchemaHandler, + ValidationError, + ValidationInfo, +) +from pydantic_core import InitErrorDetails, core_schema + +from overture.schema.foundation.constraint.constraint import Constraint + + +class CollectionConstraint(Constraint): + """Base class for collection-based constraints.""" + + def __get_pydantic_core_schema__( + self, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + # Let the handler generate the proper schema for the collection type + python_schema = handler(source) + + def validate_collection(value: Any, info: ValidationInfo) -> Any: + self.validate(value, info) + return value + + return core_schema.with_info_after_validator_function( + validate_collection, python_schema + ) + + @staticmethod + def _is_collection_type(source: type[Any]) -> bool: + origin = get_origin(source) + if origin is not None: + return issubclass(origin, Collection) + else: + return issubclass(source, Collection) + + +class UniqueItemsConstraint(CollectionConstraint): + """Constraint to ensure all items in a collection are unique.""" + + def validate(self, value: list[Any], info: ValidationInfo) -> None: + # First try the fast path for hashable items + try: + if len(value) != len(set(value)): + self._raise_duplicate_error(value, info) + except TypeError: + # Fallback for unhashable items (like lists) + if self._has_duplicates_unhashable(value): + self._raise_duplicate_error(value, info) + + def _has_duplicates_unhashable(self, value: list[Any]) -> bool: + """Check for duplicates when items are not hashable.""" + for i, item1 in enumerate(value): + for item2 in value[i + 1 :]: + if item1 == item2: + return True + return False + + def _raise_duplicate_error(self, value: list[Any], info: ValidationInfo) -> None: + """Raise validation error for duplicate items.""" + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={"error": "All items must be unique"}, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["uniqueItems"] = True + return json_schema diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/constraint.py b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/constraint.py new file mode 100644 index 000000000..11ec1e2a6 --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/constraint.py @@ -0,0 +1,29 @@ +from abc import ABC, abstractmethod +from typing import Any + +from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler, ValidationInfo +from pydantic_core import core_schema + + +class Constraint(ABC): + """Base class for all constraints.""" + + def validate(self, value: Any, info: ValidationInfo) -> None: # noqa: B027 + """Validate the value and raise ValidationError if invalid.""" + pass + + @abstractmethod + def __get_pydantic_core_schema__( + self, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + """Generate Pydantic core schema.""" + pass + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + """Generate JSON schema. + + Override in subclasses for custom schema. + """ + return handler(core_schema) diff --git a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py index 2bf713495..6e4fc674a 100644 --- a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py +++ b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py @@ -17,7 +17,7 @@ PhoneNumber, WikidataId, ) -from overture.schema.core.validation import ( +from overture.schema.foundation.constraint import ( UniqueItemsConstraint, ) from overture.schema.foundation.primitive import ( diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py index 12e3dd4a3..2522f79f0 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py @@ -20,10 +20,10 @@ ) from overture.schema.core.validation import ( ConstraintValidatedModel, - UniqueItemsConstraint, any_of, min_properties, ) +from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.foundation.primitive import float64, int32 from .enums import ( diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py index 97df57b6c..75e4bfeef 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py @@ -10,7 +10,7 @@ from overture.schema.core.models import ( Named, ) -from overture.schema.core.validation import UniqueItemsConstraint +from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.foundation.primitive import ( Geometry, GeometryType, diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py index 9522f9682..3f7080b7b 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py @@ -4,7 +4,7 @@ from pydantic import Field -from overture.schema.core.validation import UniqueItemsConstraint +from overture.schema.foundation.constraint import UniqueItemsConstraint from .models import ( AccessRestrictionRule, diff --git a/packages/overture-schema-validation/src/overture/schema/validation/__init__.py b/packages/overture-schema-validation/src/overture/schema/validation/__init__.py index e8bb0e892..a19eb5219 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/__init__.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/__init__.py @@ -27,6 +27,8 @@ class MyModel(BaseModel): tags: Annotated[list[str], UniqueItemsConstraint()] """ +from overture.schema.foundation.constraint import UniqueItemsConstraint + from .constraints import ( CategoryPatternConstraint, ConfidenceScoreConstraint, @@ -40,7 +42,6 @@ class MyModel(BaseModel): PhoneNumberConstraint, RegionCodeConstraint, StringConstraint, - UniqueItemsConstraint, WhitespaceConstraint, WikidataConstraint, ) diff --git a/packages/overture-schema-validation/src/overture/schema/validation/constraints.py b/packages/overture-schema-validation/src/overture/schema/validation/constraints.py index fd54aa766..b0fe1046e 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/constraints.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/constraints.py @@ -1,9 +1,7 @@ """Constraint-based validation for Overture Maps schemas.""" import re -from abc import ABC, abstractmethod -from collections.abc import Collection -from typing import Any, get_origin +from typing import Any from pydantic import ( GetCoreSchemaHandler, @@ -13,32 +11,10 @@ ) from pydantic_core import InitErrorDetails, core_schema +from overture.schema.foundation.constraint import CollectionConstraint, Constraint -class BaseConstraint(ABC): - """Base class for all constraints.""" - def validate(self, value: Any, info: ValidationInfo) -> None: # noqa: B027 - """Validate the value and raise ValidationError if invalid.""" - pass - - @abstractmethod - def __get_pydantic_core_schema__( - self, source: type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.CoreSchema: - """Generate Pydantic core schema.""" - pass - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - """Generate JSON schema. - - Override in subclasses for custom schema. - """ - return handler(core_schema) - - -class StringConstraint(BaseConstraint): +class StringConstraint(Constraint): """Base class for string-based constraints.""" def __get_pydantic_core_schema__( @@ -55,32 +31,6 @@ def validate_string(value: str, info: ValidationInfo) -> str: ) -class CollectionConstraint(BaseConstraint): - """Base class for collection-based constraints.""" - - def __get_pydantic_core_schema__( - self, source: type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.CoreSchema: - # Let the handler generate the proper schema for the collection type - python_schema = handler(source) - - def validate_collection(value: Any, info: ValidationInfo) -> Any: - self.validate(value, info) - return value - - return core_schema.with_info_after_validator_function( - validate_collection, python_schema - ) - - @staticmethod - def _is_collection_type(source: type[Any]) -> bool: - origin = get_origin(source) - if origin is not None: - return issubclass(origin, Collection) - else: - return issubclass(source, Collection) - - class PatternConstraint(StringConstraint): """Generic pattern-based string constraint.""" @@ -362,51 +312,6 @@ def __get_pydantic_json_schema__( return json_schema -class UniqueItemsConstraint(CollectionConstraint): - """Constraint to ensure all items in a collection are unique.""" - - def validate(self, value: list[Any], info: ValidationInfo) -> None: - # First try the fast path for hashable items - try: - if len(value) != len(set(value)): - self._raise_duplicate_error(value, info) - except TypeError: - # Fallback for unhashable items (like lists) - if self._has_duplicates_unhashable(value): - self._raise_duplicate_error(value, info) - - def _has_duplicates_unhashable(self, value: list[Any]) -> bool: - """Check for duplicates when items are not hashable.""" - for i, item1 in enumerate(value): - for item2 in value[i + 1 :]: - if item1 == item2: - return True - return False - - def _raise_duplicate_error(self, value: list[Any], info: ValidationInfo) -> None: - """Raise validation error for duplicate items.""" - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={"error": "All items must be unique"}, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["uniqueItems"] = True - return json_schema - - class CategoryPatternConstraint(StringConstraint): """Constraint for place category patterns (snake_case).""" @@ -508,7 +413,8 @@ def __get_pydantic_json_schema__( return json_schema -class ConfidenceScoreConstraint(BaseConstraint): +# TODO: Not understanding why this is needed versus just using vanilla annotation. +class ConfidenceScoreConstraint(Constraint): """Constraint for confidence/probability scores (0.0 to 1.0).""" def __get_pydantic_core_schema__( @@ -593,7 +499,7 @@ def __get_pydantic_json_schema__( return json_schema -class ExtensionPrefixModelConstraint(BaseConstraint): +class ExtensionPrefixModelConstraint(Constraint): """Constraint for models that allow only ext_* prefixed extra fields. This ensures that additional properties beyond those explicitly defined diff --git a/packages/overture-schema-validation/tests/test_constraints.py b/packages/overture-schema-validation/tests/test_constraints.py index 5e4fa5da5..72f800950 100644 --- a/packages/overture-schema-validation/tests/test_constraints.py +++ b/packages/overture-schema-validation/tests/test_constraints.py @@ -7,6 +7,7 @@ import pytest from pydantic import BaseModel, Field, ValidationError +from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.validation import ( CategoryPatternConstraint, ConfidenceScoreConstraint, @@ -19,7 +20,6 @@ PatternConstraint, PhoneNumberConstraint, RegionCodeConstraint, - UniqueItemsConstraint, WhitespaceConstraint, WikidataConstraint, ) diff --git a/packages/overture-schema-validation/tests/test_json_schema_generation.py b/packages/overture-schema-validation/tests/test_json_schema_generation.py index 9c4230215..598f5a8ec 100644 --- a/packages/overture-schema-validation/tests/test_json_schema_generation.py +++ b/packages/overture-schema-validation/tests/test_json_schema_generation.py @@ -6,6 +6,7 @@ import pytest from pydantic import BaseModel, Field +from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.validation.constraints import ( ConfidenceScoreConstraint, CountryCodeConstraint, @@ -16,7 +17,6 @@ NoWhitespaceConstraint, PatternConstraint, RegionCodeConstraint, - UniqueItemsConstraint, WhitespaceConstraint, ) from overture.schema.validation.mixin import ( From d06c41e810c3e7791ee05aac98dd6ffbbf28e177 Mon Sep 17 00:00:00 2001 From: schapper Date: Wed, 1 Oct 2025 12:43:03 -0700 Subject: [PATCH 08/20] wip - Consistency: Rename WhitespaceConstraint -> StrippedConstraint and TrimmedString -> StrippedString (stripped = Python term, not trim) --- .../overture/schema/addresses/address/models.py | 14 +++++++------- .../src/overture/schema/core/models.py | 8 ++++---- .../src/overture/schema/core/types.py | 6 +++--- .../src/overture/schema/divisions/models.py | 4 ++-- .../src/overture/schema/transportation/models.py | 12 ++++++------ packages/overture-schema-validation/README.md | 2 +- .../src/overture/schema/validation/__init__.py | 4 ++-- .../src/overture/schema/validation/constraints.py | 2 +- .../src/overture/schema/validation/types.py | 4 ++-- .../tests/test_constraints.py | 6 +++--- .../tests/test_json_schema_generation.py | 4 ++-- 11 files changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py index 7ba553a53..100406125 100644 --- a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py +++ b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py @@ -8,7 +8,7 @@ Feature, StrictBaseModel, ) -from overture.schema.core.types import CountryCode, TrimmedString +from overture.schema.core.types import CountryCode, StrippedString from overture.schema.foundation.primitive import ( Geometry, GeometryType, @@ -26,7 +26,7 @@ class AddressLevel(StrictBaseModel): """ value: Annotated[ - TrimmedString | None, + StrippedString | None, Field( min_length=1, ), @@ -67,14 +67,14 @@ class Address(Feature[Literal["addresses"], Literal["address"]]): ] = None country: CountryCode | None = None number: Annotated[ - TrimmedString | None, + StrippedString | None, Field( min_length=1, description="""The house number for this address. This field may not strictly be a number. Values such as "74B", "189 1/2", "208.5" are common as the number part of an address and they are not part of the "unit" of this address.""", ), ] = None postal_city: Annotated[ - TrimmedString | None, + StrippedString | None, Field( min_length=1, description="""In some countries or regions, a mailing address may need to specify a different city name than the city that actually contains the address coordinates. This optional field can be used to specify the alternate city name to use. @@ -87,21 +87,21 @@ class Address(Feature[Literal["addresses"], Literal["address"]]): ), ] = None postcode: Annotated[ - TrimmedString | None, + StrippedString | None, Field( min_length=1, description="The postcode for the address", ), ] = None street: Annotated[ - TrimmedString | None, + StrippedString | None, Field( min_length=1, description="""The street name associated with this address. The street name can include the street "type" or street suffix, e.g., Main Street. Ideally this is fully spelled out and not abbreviated but we acknowledge that many address datasets abbreviate the street name so it is acceptable.""", ), ] = None unit: Annotated[ - TrimmedString | None, + StrippedString | None, Field( min_length=1, description="The suite/unit/apartment/floor number", diff --git a/packages/overture-schema-core/src/overture/schema/core/models.py b/packages/overture-schema-core/src/overture/schema/core/models.py index cdaae44e5..379b9b3da 100644 --- a/packages/overture-schema-core/src/overture/schema/core/models.py +++ b/packages/overture-schema-core/src/overture/schema/core/models.py @@ -38,7 +38,7 @@ Prominence, RegionCode, SortKey, - TrimmedString, + StrippedString, ) from .validation import ConstraintValidatedModel @@ -99,7 +99,7 @@ class SourcePropertyItem(GeometricRangeScope): # Optional license: Annotated[ - TrimmedString | None, + StrippedString | None, Field( description="License name. This should be a valid SPDX license identifier when available. If the license is NULL, contact the data provider for more license information.", ), @@ -267,7 +267,7 @@ class NameRule(GeometricRangeScope, SideScope): # Required - value: Annotated[TrimmedString, Field(min_length=1)] + value: Annotated[StrippedString, Field(min_length=1)] variant: NameVariant # Optional @@ -290,7 +290,7 @@ class Names(StrictBaseModel): # Required primary: Annotated[ - TrimmedString, Field(min_length=1, description="The most commonly used name.") + StrippedString, Field(min_length=1, description="The most commonly used name.") ] # Optional diff --git a/packages/overture-schema-core/src/overture/schema/core/types.py b/packages/overture-schema-core/src/overture/schema/core/types.py index 880eaa759..f5866da37 100644 --- a/packages/overture-schema-core/src/overture/schema/core/types.py +++ b/packages/overture-schema-core/src/overture/schema/core/types.py @@ -16,7 +16,7 @@ NoWhitespaceString, PhoneNumber, RegionCode, - TrimmedString, + StrippedString, WikidataId, ) @@ -97,7 +97,7 @@ The validating regular expression for this property follows the pattern described in https://www.rfc-editor.org/rfc/bcp/bcp47.txt with the exception that private use tags are not supported.""" ), ], - TrimmedString, + StrippedString, ], Field(json_schema_extra={"additionalProperties": False}), ], @@ -211,7 +211,7 @@ "RegionCode", "SortKey", "Theme", - "TrimmedString", + "StrippedString", "Type", "WikidataId", ] diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py index 436c18079..dc1cb84a5 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py @@ -3,7 +3,7 @@ from pydantic import ConfigDict, Field from overture.schema.core import StrictBaseModel -from overture.schema.core.types import Id, TrimmedString +from overture.schema.core.types import Id, StrippedString from overture.schema.divisions.enums import PlaceType DivisionId = NewType( @@ -21,7 +21,7 @@ class HierarchyItem(StrictBaseModel): division_id: DivisionId subtype: PlaceType name: Annotated[ - TrimmedString, Field(min_length=1, description="Primary name of the division") + StrippedString, Field(min_length=1, description="Primary name of the division") ] diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py index 2522f79f0..846aa13ea 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py @@ -15,7 +15,7 @@ Level, LinearlyReferencedPosition, OpeningHours, - TrimmedString, + StrippedString, WikidataId, ) from overture.schema.core.validation import ( @@ -94,7 +94,7 @@ class DestinationLabels(StrictBaseModel): # Required value: Annotated[ - TrimmedString, + StrippedString, Field(min_length=1, description="Names the object that is reached"), ] type: DestinationLabelType @@ -155,21 +155,21 @@ class RouteReference(GeometricRangeScope): # Optional name: Annotated[ - TrimmedString | None, Field(min_length=1, description="Full name of the route") + StrippedString | None, Field(min_length=1, description="Full name of the route") ] = None network: Annotated[ - TrimmedString | None, + StrippedString | None, Field( min_length=1, description="Name of the highway system this route belongs to", ), ] = None ref: Annotated[ - TrimmedString | None, + StrippedString | None, Field(min_length=1, description="Code or number used to reference the route"), ] = None symbol: Annotated[ - TrimmedString | None, + StrippedString | None, Field(min_length=1, description="URL or description of route signage"), ] = None wikidata: WikidataId | None = None diff --git a/packages/overture-schema-validation/README.md b/packages/overture-schema-validation/README.md index 7b7c19c1f..4c05bf06f 100644 --- a/packages/overture-schema-validation/README.md +++ b/packages/overture-schema-validation/README.md @@ -122,7 +122,7 @@ from overture.schema.validation import ( RegionCode, # Annotated[str, RegionCodeConstraint()] ISO8601DateTime, # Annotated[str, ISO8601DateTimeConstraint()] JSONPointer, # Annotated[str, JSONPointerConstraint()] - TrimmedString, # Annotated[str, WhitespaceConstraint()] + StrippedString, # Annotated[str, StrippedConstraint()] HexColor, # Annotated[str, HexColorConstraint()] NoWhitespaceString, # Annotated[str, NoWhitespaceConstraint()] diff --git a/packages/overture-schema-validation/src/overture/schema/validation/__init__.py b/packages/overture-schema-validation/src/overture/schema/validation/__init__.py index a19eb5219..06f4f47c0 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/__init__.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/__init__.py @@ -42,7 +42,7 @@ class MyModel(BaseModel): PhoneNumberConstraint, RegionCodeConstraint, StringConstraint, - WhitespaceConstraint, + StrippedConstraint, WikidataConstraint, ) from .mixin import ( @@ -70,7 +70,7 @@ class MyModel(BaseModel): "RegionCodeConstraint", "StringConstraint", "UniqueItemsConstraint", - "WhitespaceConstraint", + "StrippedConstraint", "WikidataConstraint", "allow_extension_fields", "any_of", diff --git a/packages/overture-schema-validation/src/overture/schema/validation/constraints.py b/packages/overture-schema-validation/src/overture/schema/validation/constraints.py index b0fe1046e..fbc390355 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/constraints.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/constraints.py @@ -282,7 +282,7 @@ def __get_pydantic_json_schema__( return json_schema -class WhitespaceConstraint(StringConstraint): +class StrippedConstraint(StringConstraint): """Constraint to ensure string has no leading/trailing whitespace.""" def validate(self, value: str, info: ValidationInfo) -> None: diff --git a/packages/overture-schema-validation/src/overture/schema/validation/types.py b/packages/overture-schema-validation/src/overture/schema/validation/types.py index e29191895..8266f0ffe 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/types.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/types.py @@ -16,7 +16,7 @@ NoWhitespaceConstraint, PhoneNumberConstraint, RegionCodeConstraint, - WhitespaceConstraint, + StrippedConstraint, WikidataConstraint, ) @@ -45,7 +45,7 @@ Field(description="ISO 3166-2 principal subdivision code."), ], ) -TrimmedString = NewType("TrimmedString", Annotated[str, WhitespaceConstraint()]) +StrippedString = NewType("StrippedString", Annotated[str, StrippedConstraint()]) WikidataId = NewType( "WikidataId", Annotated[ diff --git a/packages/overture-schema-validation/tests/test_constraints.py b/packages/overture-schema-validation/tests/test_constraints.py index 72f800950..d2bf8865b 100644 --- a/packages/overture-schema-validation/tests/test_constraints.py +++ b/packages/overture-schema-validation/tests/test_constraints.py @@ -20,7 +20,7 @@ PatternConstraint, PhoneNumberConstraint, RegionCodeConstraint, - WhitespaceConstraint, + StrippedConstraint, WikidataConstraint, ) from overture.schema.validation.types import ( @@ -182,7 +182,7 @@ def test_whitespace_constraint_valid(self) -> None: whitespace).""" class TestModel(BaseModel): - text: Annotated[str, WhitespaceConstraint()] + text: Annotated[str, StrippedConstraint()] valid_strings = [ "hello", @@ -200,7 +200,7 @@ def test_whitespace_constraint_invalid(self) -> None: whitespace).""" class TestModel(BaseModel): - text: Annotated[str, WhitespaceConstraint()] + text: Annotated[str, StrippedConstraint()] invalid_strings = [ " hello", # Leading space diff --git a/packages/overture-schema-validation/tests/test_json_schema_generation.py b/packages/overture-schema-validation/tests/test_json_schema_generation.py index 598f5a8ec..a27ef69ad 100644 --- a/packages/overture-schema-validation/tests/test_json_schema_generation.py +++ b/packages/overture-schema-validation/tests/test_json_schema_generation.py @@ -17,7 +17,7 @@ NoWhitespaceConstraint, PatternConstraint, RegionCodeConstraint, - WhitespaceConstraint, + StrippedConstraint, ) from overture.schema.validation.mixin import ( ConstraintValidatedModel, @@ -353,7 +353,7 @@ def test_json_pointer_constraint_json_schema(self) -> None: def test_whitespace_constraint_json_schema(self) -> None: """Test WhitespaceConstraint JSON schema generation.""" - constraint = WhitespaceConstraint() + constraint = StrippedConstraint() TestModel = create_field_constraint_model(str, constraint) schema = TestModel.model_json_schema() From dcb73d8fa869aa2a85f66dec84db7d5181cc7276 Mon Sep 17 00:00:00 2001 From: schapper Date: Wed, 1 Oct 2025 15:31:52 -0700 Subject: [PATCH 09/20] wip - Migrate string types/constraints out of validation package --- gers/examples/python/constants.py | 1 - .../schema/addresses/address/models.py | 3 +- .../src/overture/schema/base/models.py | 2 +- .../src/overture/schema/buildings/models.py | 2 +- .../src/overture/schema/core/models.py | 10 +- .../src/overture/schema/core/types.py | 14 +- .../schema/divisions/division/models.py | 3 +- .../schema/divisions/division_area/models.py | 3 +- .../divisions/division_boundary/models.py | 2 +- .../src/overture/schema/divisions/models.py | 3 +- .../schema/foundation/constraint/__init__.py | 28 +- .../schema/foundation/constraint/string.py | 376 ++++++++++++ .../src/overture/schema/foundation/string.py | 152 +++++ .../constraint/test_collection_constraints.py | 67 +++ .../constraint/test_string_constraints.py | 424 ++++++++++++++ .../tests/test_string.py | 61 ++ .../overture/schema/places/place/models.py | 3 +- .../overture/schema/transportation/models.py | 3 +- .../overture/schema/validation/__init__.py | 23 - .../overture/schema/validation/constraints.py | 363 +----------- .../src/overture/schema/validation/types.py | 44 -- .../tests/test_constraints.py | 538 +----------------- .../tests/test_json_schema_generation.py | 8 +- 23 files changed, 1142 insertions(+), 991 deletions(-) create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/constraint/string.py create mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/string.py create mode 100644 packages/overture-schema-foundation/tests/constraint/test_collection_constraints.py create mode 100644 packages/overture-schema-foundation/tests/constraint/test_string_constraints.py create mode 100644 packages/overture-schema-foundation/tests/test_string.py diff --git a/gers/examples/python/constants.py b/gers/examples/python/constants.py index 24343934e..34776bb74 100644 --- a/gers/examples/python/constants.py +++ b/gers/examples/python/constants.py @@ -1,4 +1,3 @@ - DEFAULT_H3_RESOLUTION = 12 # default params for nearest match diff --git a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py index 100406125..0920af5d2 100644 --- a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py +++ b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py @@ -8,12 +8,13 @@ Feature, StrictBaseModel, ) -from overture.schema.core.types import CountryCode, StrippedString +from overture.schema.core.types import CountryCode from overture.schema.foundation.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, ) +from overture.schema.foundation.string import StrippedString class AddressLevel(StrictBaseModel): diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/models.py index a8a33d7da..551c72ec4 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/models.py @@ -1,7 +1,7 @@ from pydantic import BaseModel from overture.schema.base.types import SourceTags -from overture.schema.core.types import WikidataId +from overture.schema.foundation.string import WikidataId class SourcedFromOpenStreetMap(BaseModel): diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py index 6950f82f8..e519eee89 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py @@ -8,8 +8,8 @@ RoofOrientation, RoofShape, ) -from overture.schema.core.types import HexColor from overture.schema.foundation.primitive import float64, int32 +from overture.schema.foundation.string import HexColor class Shape(BaseModel): diff --git a/packages/overture-schema-core/src/overture/schema/core/models.py b/packages/overture-schema-core/src/overture/schema/core/models.py index 379b9b3da..03c9f5421 100644 --- a/packages/overture-schema-core/src/overture/schema/core/models.py +++ b/packages/overture-schema-core/src/overture/schema/core/models.py @@ -17,6 +17,12 @@ BBox, Geometry, ) +from overture.schema.foundation.string import ( + JsonPointer, + LanguageTag, + RegionCode, + StrippedString, +) from overture.schema.validation import ( allow_extension_fields, ) @@ -29,16 +35,12 @@ FeatureUpdateTime, FeatureVersion, Id, - JsonPointer, - LanguageTag, Level, LinearlyReferencedRange, MaxZoom, MinZoom, Prominence, - RegionCode, SortKey, - StrippedString, ) from .validation import ConstraintValidatedModel diff --git a/packages/overture-schema-core/src/overture/schema/core/types.py b/packages/overture-schema-core/src/overture/schema/core/types.py index f5866da37..e277d4f8e 100644 --- a/packages/overture-schema-core/src/overture/schema/core/types.py +++ b/packages/overture-schema-core/src/overture/schema/core/types.py @@ -3,13 +3,11 @@ from pydantic import Field -from overture.schema.foundation.primitive import int32, pct -from overture.schema.validation.constraints import ( +from overture.schema.foundation.constraint.string import ( CountryCodeConstraint, - LinearReferenceRangeConstraint, ) -from overture.schema.validation.types import ( - ConfidenceScore, +from overture.schema.foundation.primitive import int32, pct +from overture.schema.foundation.string import ( HexColor, JsonPointer, LanguageTag, @@ -19,6 +17,12 @@ StrippedString, WikidataId, ) +from overture.schema.validation.constraints import ( + LinearReferenceRangeConstraint, +) +from overture.schema.validation.types import ( + ConfidenceScore, +) Id = NewType( "Id", diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py index 585c254d3..dfa1f718f 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py @@ -19,8 +19,6 @@ CommonNames, CountryCode, Id, - RegionCode, - WikidataId, ) from overture.schema.foundation.constraint import ( UniqueItemsConstraint, @@ -31,6 +29,7 @@ GeometryTypeConstraint, int32, ) +from overture.schema.foundation.string import RegionCode, WikidataId from ..enums import DivisionClass, PlaceType from ..models import CapitalOfDivisionItem diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py index ab0aa96ec..0017186e7 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py @@ -12,7 +12,7 @@ Names, ) from overture.schema.core.ref import Reference, Relationship -from overture.schema.core.types import CountryCode, Id, RegionCode +from overture.schema.core.types import CountryCode, Id from overture.schema.core.validation import ( exactly_one_of, ) @@ -21,6 +21,7 @@ GeometryType, GeometryTypeConstraint, ) +from overture.schema.foundation.string import RegionCode from ..division.models import Division from ..enums import PlaceType diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py index a58e4946a..5a246cf49 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py @@ -12,7 +12,6 @@ from overture.schema.core.types import ( CountryCode, Id, - RegionCode, ) from overture.schema.core.validation import ( exactly_one_of, @@ -24,6 +23,7 @@ GeometryType, GeometryTypeConstraint, ) +from overture.schema.foundation.string import RegionCode from ..division import Division from ..enums import PlaceType diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py index dc1cb84a5..fdd64efda 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py @@ -3,8 +3,9 @@ from pydantic import ConfigDict, Field from overture.schema.core import StrictBaseModel -from overture.schema.core.types import Id, StrippedString +from overture.schema.core.types import Id from overture.schema.divisions.enums import PlaceType +from overture.schema.foundation.string import StrippedString DivisionId = NewType( "DivisionId", Annotated[Id, Field(min_length=1, description="ID of the division")] diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py index 8f2e9187c..1f9042b03 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py @@ -11,7 +11,9 @@ constraint : module Base constraint type. collection : module - Constraints for collections + Constraints for collection fields. +string : module + Constraints for string fields. """ @@ -20,9 +22,33 @@ UniqueItemsConstraint, ) from .constraint import Constraint +from .string import ( + CountryCodeConstraint, + HexColorConstraint, + JsonPointerConstraint, + LanguageTagConstraint, + NoWhitespaceConstraint, + PatternConstraint, + PhoneNumberConstraint, + RegionCodeConstraint, + StringConstraint, + StrippedConstraint, + WikidataIdConstraint, +) __all__ = [ "Constraint", "CollectionConstraint", "UniqueItemsConstraint", + "CountryCodeConstraint", + "HexColorConstraint", + "JsonPointerConstraint", + "LanguageTagConstraint", + "NoWhitespaceConstraint", + "PatternConstraint", + "PhoneNumberConstraint", + "RegionCodeConstraint", + "StringConstraint", + "StrippedConstraint", + "WikidataIdConstraint", ] diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/string.py b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/string.py new file mode 100644 index 000000000..0a6479502 --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/string.py @@ -0,0 +1,376 @@ +import re +from typing import Any + +from pydantic import ( + GetCoreSchemaHandler, + GetJsonSchemaHandler, + ValidationError, + ValidationInfo, +) +from pydantic_core import InitErrorDetails, core_schema + +from overture.schema.foundation.constraint import ( + Constraint, +) + + +class StringConstraint(Constraint): + """Base class for string-based constraints.""" + + def __get_pydantic_core_schema__( + self, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + python_schema = handler(str) + + def validate_string(value: str, info: ValidationInfo) -> str: + self.validate(value, info) + return value + + return core_schema.with_info_after_validator_function( + validate_string, python_schema + ) + + +class PatternConstraint(StringConstraint): + """Generic pattern-based string constraint.""" + + def __init__(self, pattern: str, error_message: str, flags: int = 0): + self.pattern = re.compile(pattern, flags) + self.pattern_str = pattern + self.error_message = error_message + + def validate(self, value: str, info: ValidationInfo) -> None: + if not self.pattern.match(value): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={"error": self.error_message.format(value=value)}, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = self.pattern_str + return json_schema + + +class LanguageTagConstraint(StringConstraint): + """This pattern recognizes BCP-47 language tags at the lexical or syntactic level. + It verifies that candidate tags follow the grammar described in the RFC, but not + that they are validly registered tag in IANA's language subtag registry. + + In understanding the regular expression, remark that '(:?' indicates a non-capturing + group, and that all the top-level or non-nested groups represent top-level + components of `langtag` referenced in the syntax section of + https://www.rfc-editor.org/rfc/bcp/bcp47.txt. In particular, the top-level groups in + left-to-right order represent: + + 1. language + 2. ["-" script] + 3. ["-" region] + 4. *("-" variant) + 5. *("-" extension) + """ + + def __init__(self) -> None: + self.pattern = re.compile( + r"^(?:(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}?)|(?:[A-Za-z]{4,8}))(?:-[A-Za-z]{4})?(?:-[A-Za-z]{2}|[0-9]{3})?(?:-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(?:-[A-WY-Za-wy-z0-9](?:-[A-Za-z0-9]{2,8})+)*$" + ) + + def validate(self, value: str, info: ValidationInfo) -> None: + if not self.pattern.match(value): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={"error": f"Invalid IETF BCP-47 language tag: {value}"}, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = self.pattern.pattern + json_schema["description"] = "IETF BCP-47 language tag" + return json_schema + + +class CountryCodeConstraint(StringConstraint): + """ISO 3166-1 alpha-2 country code constraint.""" + + def __init__(self) -> None: + self.pattern = re.compile(r"^[A-Z]{2}$") + + def validate(self, value: str, info: ValidationInfo) -> None: + if not self.pattern.match(value): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={ + "error": f"Invalid ISO 3166-1 alpha-2 country code: {value}" + }, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = self.pattern.pattern + json_schema["minLength"] = 2 + json_schema["maxLength"] = 2 + json_schema["description"] = "ISO 3166-1 alpha-2 country code" + return json_schema + + +class RegionCodeConstraint(StringConstraint): + """ISO 3166-2 principal subdivision code constraint.""" + + def __init__(self) -> None: + self.pattern = re.compile(r"^[A-Z]{2}-[A-Z0-9]{1,3}$") + + def validate(self, value: str, info: ValidationInfo) -> None: + if not self.pattern.match(value): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={"error": f"Invalid ISO 3166-2 subdivision code: {value}"}, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = self.pattern.pattern + json_schema["minLength"] = 4 + json_schema["maxLength"] = 6 + json_schema["description"] = "ISO 3166-2 subdivision code" + return json_schema + + +class JsonPointerConstraint(StringConstraint): + """JSON Pointer constraint (RFC 6901).""" + + def validate(self, value: str, info: ValidationInfo) -> None: + # Empty string represents root pointer + if value == "": + return + + if not value.startswith("/"): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={ + "error": f"JSON Pointer must start with '/' or be empty string: {value}" + }, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["description"] = "JSON Pointer (RFC 6901)" + return json_schema + + +class StrippedConstraint(StringConstraint): + """Constraint to ensure string has no leading/trailing whitespace.""" + + def validate(self, value: str, info: ValidationInfo) -> None: + if value != value.strip(): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={ + "error": f"String cannot have leading or trailing whitespace: {repr(value)}" + }, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = r"^(\S.*)?\S$" + json_schema["description"] = "String with no leading/trailing whitespace" + return json_schema + + +class WikidataIdConstraint(StringConstraint): + """Constraint for Wikidata identifiers (Q followed by digits).""" + + def __init__(self) -> None: + self.pattern = re.compile(r"^Q\d+$") + + def validate(self, value: str, info: ValidationInfo) -> None: + if not self.pattern.match(value): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={ + "error": f"Invalid Wikidata identifier: {value}. Must be Q followed by digits (e.g., Q123)" + }, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = self.pattern.pattern + json_schema["description"] = "Wikidata identifier (Q followed by digits)" + return json_schema + + +class PhoneNumberConstraint(StringConstraint): + """Constraint for international phone numbers.""" + + def __init__(self) -> None: + self.pattern = re.compile(r"^\+\d{1,3}[\s\-\(\)0-9]+$") + + def validate(self, value: str, info: ValidationInfo) -> None: + if not self.pattern.match(value): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={ + "error": f"Invalid phone number format: {value}. Must start with + and country code" + }, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = self.pattern.pattern + json_schema["description"] = ( + "International phone number (+ followed by country code and number)" + ) + return json_schema + + +class HexColorConstraint(StringConstraint): + """Constraint for hexadecimal color codes (e.g., #FF0000 or #FFF).""" + + def __init__(self) -> None: + self.pattern = re.compile(r"^#[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$") + + def validate(self, value: str, info: ValidationInfo) -> None: + if not self.pattern.match(value): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={ + "error": f"Invalid hexadecimal color format: {value}. Must be in format #RGB or #RRGGBB (e.g., #FFF or #FF0000)" + }, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = self.pattern.pattern + json_schema["description"] = "Hexadecimal color code in format #RGB or #RRGGBB" + return json_schema + + +class NoWhitespaceConstraint(StringConstraint): + """Constraint for strings that cannot contain whitespace characters.""" + + def __init__(self) -> None: + self.pattern = re.compile(r"^\S+$") + + def validate(self, value: str, info: ValidationInfo) -> None: + if not self.pattern.match(value): + context = info.context or {} + loc = context.get("loc_prefix", ()) + ("value",) + raise ValidationError.from_exception_data( + title=self.__class__.__name__, + line_errors=[ + InitErrorDetails( + type="value_error", + loc=loc, + input=value, + ctx={ + "error": f"String cannot contain whitespace characters: '{value}'" + }, + ) + ], + ) + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> dict[str, Any]: + json_schema = handler(core_schema) + json_schema["pattern"] = self.pattern.pattern + json_schema["description"] = "String without whitespace characters" + return json_schema diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/string.py b/packages/overture-schema-foundation/src/overture/schema/foundation/string.py new file mode 100644 index 000000000..637481726 --- /dev/null +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/string.py @@ -0,0 +1,152 @@ +from typing import Annotated, NewType + +from pydantic import Field + +from overture.schema.foundation.constraint import ( + CountryCodeConstraint, + HexColorConstraint, + JsonPointerConstraint, + LanguageTagConstraint, + NoWhitespaceConstraint, + PhoneNumberConstraint, + RegionCodeConstraint, + StrippedConstraint, + WikidataIdConstraint, +) + +HexColor = NewType( + "HexColor", + Annotated[ + str, + HexColorConstraint(), + Field( + description="A color represented as an #RRGGBB or #RGB hexadecimal string, for example #ff0000 for pure red." + ), + ], +) +HexColor.__doc__ = """ +HexColor : NewType + A color represented as an #RRGGBB or #RGB hexadecimal string. + + For example: + + - "#ff0000" for pure red 🟥 + - "#ffa500" for bright orange 🟧 + - "#000000" or "#000" for black ⬛ +""" + + +JsonPointer = NewType( + "JsonPointer", + Annotated[ + str, + JsonPointerConstraint(), + Field(description="A JSON Pointer (as described in RFC-6901)"), + ], +) +JsonPointer.__doc__ = """ +JsonPointer : NewType + A JSON Pointer + + As described in `the JSON Pointer specification, RFC-6901 ` +""" + + +LanguageTag = NewType( + "LanguageTag", + Annotated[ + str, + LanguageTagConstraint(), + Field( + description="A BCP-47 language tag", + ), + ], +) +LanguageTag.__doc__ = """ +LanguageTag : NewType + A BCP-47 language tag. + + As described in `Tags for Identifying Languages, BCP-47 ` +""" + + +StrippedString = NewType( + "StrippedString", + Annotated[ + str, + StrippedConstraint(), + Field( + description="A string without leading or trailing whitespace", + ), + ], +) +StrippedString.__doc__ = """ +StrippedString : NewType + A string without leading or trailing whitespace. +""" + + +NoWhitespaceString = NewType( + "NoWhitespaceString", + Annotated[ + str, + NoWhitespaceConstraint(), + Field(description="A string that contains no whitespace characters"), + ], +) +NoWhitespaceString.__doc__ = """ +NoWhitespaceString : NewType + A string that contains no whitespace characters. +""" + + +CountryCode = NewType( + "CountryCode", + Annotated[ + str, + CountryCodeConstraint(), + Field(description="An ISO 3166-1 alpha-2 country code"), + ], +) +CountryCode.__doc__ = """ +CountryCode : NewType + An ISO-316601 alpha-2 country code. +""" + +RegionCode = NewType( + "RegionCode", + Annotated[ + str, + RegionCodeConstraint(), + Field(description="An ISO 3166-2 principal subdivision code"), + ], +) +RegionCode.__doc__ = """ +RegionCode : NewType + An ISO 3166-2 principal subdivision code. +""" + +WikidataId = NewType( + "WikidataId", + Annotated[ + str, + WikidataIdConstraint(), + Field(description="A wikidata ID, as found on https://www.wikidata.org/"), + ], +) +WikidataId.__doc__ = """ +WikidataId : NewType + A wikidata ID, as found on https://www.wikidata.org/. +""" + + +PhoneNumber = NewType( + "PhoneNumber", + Annotated[ + str, PhoneNumberConstraint(), Field(description="An international phone number") + ], +) +PhoneNumber.__doc__ = """ +PhoneNumber : NewType + An international telephone number. +""" diff --git a/packages/overture-schema-foundation/tests/constraint/test_collection_constraints.py b/packages/overture-schema-foundation/tests/constraint/test_collection_constraints.py new file mode 100644 index 000000000..1cfaa0137 --- /dev/null +++ b/packages/overture-schema-foundation/tests/constraint/test_collection_constraints.py @@ -0,0 +1,67 @@ +from typing import Annotated + +import pytest +from pydantic import BaseModel, Field, ValidationError + +from overture.schema.foundation.constraint import UniqueItemsConstraint + + +class TestUniqueItemsConstraint: + """Test all collection-based constraints.""" + + def test_valid(self) -> None: + """Test UniqueItemsConstraint with unique items.""" + + class TestModel(BaseModel): + tags: Annotated[list[str], UniqueItemsConstraint()] + + valid_lists = [ + [], + ["a"], + ["a", "b", "c"], + ["unique", "items", "only"], + ] + + for items in valid_lists: + model = TestModel(tags=items) + assert model.tags == items + + def test_duplicates(self) -> None: + """Test UniqueItemsConstraint with duplicate items.""" + + class TestModel(BaseModel): + tags: Annotated[list[str], UniqueItemsConstraint()] + + invalid_lists = [ + ["a", "a"], + ["a", "b", "a"], + ["duplicate", "values", "duplicate"], + ] + + for items in invalid_lists: + with pytest.raises(ValidationError) as exc_info: + TestModel(tags=items) + assert "All items must be unique" in str(exc_info.value) + + def test_json_schema(self) -> None: + """Test that the unique items constraint generates proper JSON Schema.""" + + def test_collection_constraints_json_schema(self) -> None: + class TestModel(BaseModel): + unique_items: Annotated[list[str], UniqueItemsConstraint()] + min_items: Annotated[ + list[str], UniqueItemsConstraint(), Field(min_length=2) + ] + max_items: Annotated[ + list[str], UniqueItemsConstraint(), Field(max_length=5) + ] + + schema = TestModel.model_json_schema() + props = schema["properties"] + + # Check collection constraints + assert props["unique_items"].get("uniqueItems") is True + assert props["min_items"].get("uniqueItems") is True + assert props["min_items"].get("minItems") == 2 + assert props["max_items"].get("uniqueItems") is True + assert props["max_items"].get("maxItems") == 5 diff --git a/packages/overture-schema-foundation/tests/constraint/test_string_constraints.py b/packages/overture-schema-foundation/tests/constraint/test_string_constraints.py new file mode 100644 index 000000000..91033d132 --- /dev/null +++ b/packages/overture-schema-foundation/tests/constraint/test_string_constraints.py @@ -0,0 +1,424 @@ +from typing import Annotated + +import pytest +from pydantic import BaseModel, ValidationError + +from overture.schema.foundation.constraint.string import ( + CountryCodeConstraint, + HexColorConstraint, + JsonPointerConstraint, + LanguageTagConstraint, + NoWhitespaceConstraint, + PatternConstraint, + PhoneNumberConstraint, + RegionCodeConstraint, + StrippedConstraint, + WikidataIdConstraint, +) + + +class TestStringConstraints: + """Test all string-based constraints.""" + + def test_pattern_constraint_valid(self) -> None: + """Test PatternConstraint with valid values.""" + constraint = PatternConstraint(r"^[A-Z]{2}$", "Must be 2 uppercase letters") + + class TestModel(BaseModel): + code: Annotated[str, constraint] + + # Valid values + model = TestModel(code="US") + assert model.code == "US" + + model = TestModel(code="GB") + assert model.code == "GB" + + def test_pattern_constraint_invalid(self) -> None: + """Test PatternConstraint with invalid values.""" + constraint = PatternConstraint(r"^[A-Z]{2}$", "Must be 2 uppercase letters") + + class TestModel(BaseModel): + code: Annotated[str, constraint] + + # Invalid values + with pytest.raises(ValidationError) as exc_info: + TestModel(code="usa") + assert "Must be 2 uppercase letters" in str(exc_info.value) + + with pytest.raises(ValidationError): + TestModel(code="123") + + def test_language_tag_constraint_valid(self) -> None: + """Test LanguageTagConstraint with valid language tags.""" + + class TestModel(BaseModel): + language: Annotated[str, LanguageTagConstraint()] + + # Valid language tags + valid_tags = ["en", "en-US", "en-GB", "zh-CN", "fr-CA", "es-MX"] + + for tag in valid_tags: + model = TestModel(language=tag) + assert model.language == tag + + def test_language_tag_constraint_invalid(self) -> None: + """Test LanguageTagConstraint with invalid language tags.""" + + class TestModel(BaseModel): + language: Annotated[str, LanguageTagConstraint()] + + invalid_tags = ["invalid-tag-format", "123", "en_US", "toolongcode"] + + for tag in invalid_tags: + with pytest.raises(ValidationError) as exc_info: + TestModel(language=tag) + assert "Invalid IETF BCP-47 language tag" in str(exc_info.value) + + def test_country_code_constraint_valid(self) -> None: + """Test CountryCodeConstraint with valid ISO 3166-1 alpha-2 codes.""" + + class TestModel(BaseModel): + country: Annotated[str, CountryCodeConstraint()] + + valid_codes = ["US", "GB", "CA", "FR", "DE", "JP", "CN", "BR"] + + for code in valid_codes: + model = TestModel(country=code) + assert model.country == code + + def test_country_code_constraint_invalid(self) -> None: + """Test CountryCodeConstraint with invalid country codes.""" + + class TestModel(BaseModel): + country: Annotated[str, CountryCodeConstraint()] + + invalid_codes = ["USA", "123", "invalid", "gb", "us"] + + for code in invalid_codes: + with pytest.raises(ValidationError) as exc_info: + TestModel(country=code) + assert "Invalid ISO 3166-1 alpha-2 country code" in str(exc_info.value) + + def test_region_code_constraint_valid(self) -> None: + """Test RegionCodeConstraint with valid ISO 3166-2 codes.""" + + class TestModel(BaseModel): + region: Annotated[str, RegionCodeConstraint()] + + valid_codes = ["US-CA", "GB-ENG", "CA-ON", "FR-75", "DE-BY"] + + for code in valid_codes: + model = TestModel(region=code) + assert model.region == code + + def test_region_code_constraint_invalid(self) -> None: + """Test RegionCodeConstraint with invalid region codes.""" + + class TestModel(BaseModel): + region: Annotated[str, RegionCodeConstraint()] + + invalid_codes = ["US", "123-45", "invalid-region", "us-ca"] + + for code in invalid_codes: + with pytest.raises(ValidationError) as exc_info: + TestModel(region=code) + assert "Invalid ISO 3166-2 subdivision code" in str(exc_info.value) + + def test_json_pointer_constraint_valid(self) -> None: + """Test JsonPointerConstraint with valid JSON pointers.""" + + class TestModel(BaseModel): + pointer: Annotated[str, JsonPointerConstraint()] + + valid_pointers = [ + "", + "/foo", + "/foo/bar", + "/0", + "/foo/0/bar", + "/~0", # Represents ~ + "/~1", # Represents / + ] + + for ptr in valid_pointers: + model = TestModel(pointer=ptr) + assert model.pointer == ptr + + def test_json_pointer_constraint_invalid(self) -> None: + """Test JsonPointerConstraint with invalid JSON pointers.""" + + class TestModel(BaseModel): + pointer: Annotated[str, JsonPointerConstraint()] + + invalid_pointers = [ + "foo", # Must start with / + "foo/bar", # Must start with / + ] + + for ptr in invalid_pointers: + with pytest.raises(ValidationError) as exc_info: + TestModel(pointer=ptr) + assert "JSON Pointer must start" in str(exc_info.value) + + def test_whitespace_constraint_valid(self) -> None: + """Test WhitespaceConstraint with valid strings (no leading/trailing + whitespace).""" + + class TestModel(BaseModel): + text: Annotated[str, StrippedConstraint()] + + valid_strings = [ + "hello", + "hello world", + "text with internal spaces", + "", # Empty string is valid + ] + + for text in valid_strings: + model = TestModel(text=text) + assert model.text == text + + def test_whitespace_constraint_invalid(self) -> None: + """Test WhitespaceConstraint with invalid strings (leading/trailing + whitespace).""" + + class TestModel(BaseModel): + text: Annotated[str, StrippedConstraint()] + + invalid_strings = [ + " hello", # Leading space + "hello ", # Trailing space + "\thello", # Leading tab + "hello\n", # Trailing newline + " hello world ", # Both leading and trailing + ] + + for text in invalid_strings: + with pytest.raises(ValidationError) as exc_info: + TestModel(text=text) + assert "cannot have leading or trailing whitespace" in str(exc_info.value) + + def test_wikidata_constraint_valid(self) -> None: + """Test WikidataConstraint with valid Wikidata identifiers.""" + + class TestModel(BaseModel): + wikidata_id: Annotated[str, WikidataIdConstraint()] + + valid_ids = ["Q1", "Q123", "Q999999", "Q1234567890"] + + for wid in valid_ids: + model = TestModel(wikidata_id=wid) + assert model.wikidata_id == wid + + def test_wikidata_constraint_invalid(self) -> None: + """Test WikidataConstraint with invalid Wikidata identifiers.""" + + class TestModel(BaseModel): + wikidata_id: Annotated[str, WikidataIdConstraint()] + + invalid_ids = [ + "q123", # Lowercase q + "P123", # Property instead of item + "Q", # Missing number + "123", # Missing Q prefix + "Q12abc", # Non-numeric suffix + ] + + for wid in invalid_ids: + with pytest.raises(ValidationError) as exc_info: + TestModel(wikidata_id=wid) + assert "Invalid Wikidata identifier" in str(exc_info.value) + + def test_phone_number_constraint_valid(self) -> None: + """Test PhoneNumberConstraint with valid international phone numbers.""" + + class TestModel(BaseModel): + phone: Annotated[str, PhoneNumberConstraint()] + + valid_phones = [ + "+1-555-123-4567", + "+44-20-7946-0958", + "+33-1-42-86-83-26", + "+81-3-1234-5678", + "+86-10-8888-8888", + ] + + for phone in valid_phones: + model = TestModel(phone=phone) + assert model.phone == phone + + def test_phone_number_constraint_invalid(self) -> None: + """Test PhoneNumberConstraint with invalid phone numbers.""" + + class TestModel(BaseModel): + phone: Annotated[str, PhoneNumberConstraint()] + + invalid_phones = [ + "555-123-4567", # Missing country code + "1-555-123-4567", # Missing + + "not-a-phone", # Not a phone number + ] + + for phone in invalid_phones: + with pytest.raises(ValidationError) as exc_info: + TestModel(phone=phone) + assert "Invalid phone number format" in str(exc_info.value) + + def test_hex_color_constraint_valid(self) -> None: + """Test HexColorConstraint with valid hex colors.""" + + class TestModel(BaseModel): + color: Annotated[str, HexColorConstraint()] + + valid_colors = [ + "#FFFFFF", + "#000000", + "#FF0000", + "#00FF00", + "#0000FF", + "#ABCDEF", + "#123456", + "#ffffff", # lowercase + "#abcdef", # lowercase + "#FFF", # 3-character uppercase + "#fff", # 3-character lowercase + "#ABC", # 3-character mixed case + "#123", # 3-character numbers + ] + + for color in valid_colors: + model = TestModel(color=color) + assert model.color == color + + def test_hex_color_constraint_invalid(self) -> None: + """Test HexColorConstraint with invalid hex colors.""" + + class TestModel(BaseModel): + color: Annotated[str, HexColorConstraint()] + + invalid_colors = [ + "FFFFFF", # Missing # + "#FF", # Too short (2 chars) + "#FFFFFFF", # Too long (7 chars) + "#GGGGGG", # Invalid hex characters + "red", # Not hex + "#", # Just hash + "#FFFF", # Invalid length (4 chars) + ] + + for color in invalid_colors: + with pytest.raises(ValidationError) as exc_info: + TestModel(color=color) + # Just check that validation fails - message may vary + assert len(exc_info.value.errors()) > 0 + + def test_no_whitespace_constraint_valid(self) -> None: + """Test NoWhitespaceConstraint with valid strings (no whitespace).""" + + class TestModel(BaseModel): + identifier: Annotated[str, NoWhitespaceConstraint()] + + valid_identifiers = [ + "hello", + "identifier123", + "snake_case_id", + "kebab-case-id", + "camelCaseId", + ] + + for ident in valid_identifiers: + model = TestModel(identifier=ident) + assert model.identifier == ident + + def test_no_whitespace_constraint_invalid(self) -> None: + """Test NoWhitespaceConstraint with invalid strings (containing whitespace).""" + + class TestModel(BaseModel): + identifier: Annotated[str, NoWhitespaceConstraint()] + + invalid_identifiers = [ + "hello world", + "id with spaces", + "tab\tcharacter", + "new\nline", + "carriage\rreturn", + ] + + for ident in invalid_identifiers: + with pytest.raises(ValidationError) as exc_info: + TestModel(identifier=ident) + # Just check that validation fails - message may vary + assert len(exc_info.value.errors()) > 0 + + +class TestJsonSchemaGeneration: + """Test JSON schema generation for all constraints.""" + + def test_string_constraints_json_schema(self) -> None: + """Test that string constraints generate proper JSON schema.""" + + class TestModel(BaseModel): + language: Annotated[str, LanguageTagConstraint()] + country: Annotated[str, CountryCodeConstraint()] + color: Annotated[str, HexColorConstraint()] + + schema = TestModel.model_json_schema() + props = schema["properties"] + + # Check that patterns are included + assert "pattern" in props["language"] + assert "pattern" in props["country"] + assert "pattern" in props["color"] + + # Check descriptions + assert "IETF BCP-47 language tag" in props["language"].get("description", "") + + +class TestErrorHandling: + """Test error handling and validation context.""" + + def test_validation_error_context(self) -> None: + """Test that validation errors include proper context and location info.""" + + class TestModel(BaseModel): + country: Annotated[str, CountryCodeConstraint()] + + with pytest.raises(ValidationError) as exc_info: + TestModel(country="invalid") + + error = exc_info.value + assert error.error_count() == 1 + + error_detail = error.errors()[0] + assert error_detail["type"] == "value_error" + assert error_detail["input"] == "invalid" + assert "Invalid ISO 3166-1 alpha-2 country code" in str( + error_detail["ctx"]["error"] + ) + + def test_nested_validation_errors(self) -> None: + """Test validation errors in nested structures.""" + + class NestedModel(BaseModel): + country: Annotated[str, CountryCodeConstraint()] + + class TestModel(BaseModel): + nested: NestedModel + items: list[NestedModel] + + # Test nested object error + with pytest.raises(ValidationError) as exc_info: + TestModel(nested=NestedModel(country="invalid"), items=[]) + + error = exc_info.value + assert error.error_count() >= 1 + + # Test nested array error + with pytest.raises(ValidationError) as exc_info: + TestModel( + nested=NestedModel(country="US"), items=[NestedModel(country="invalid")] + ) + + error = exc_info.value + assert error.error_count() >= 1 diff --git a/packages/overture-schema-foundation/tests/test_string.py b/packages/overture-schema-foundation/tests/test_string.py new file mode 100644 index 000000000..4ba4c58f8 --- /dev/null +++ b/packages/overture-schema-foundation/tests/test_string.py @@ -0,0 +1,61 @@ +from datetime import datetime + +import pytest +from pydantic import BaseModel, ValidationError + +from overture.schema.foundation.string import ( + CountryCode, + HexColor, + JsonPointer, + LanguageTag, + RegionCode, +) + + +class TestConstrainedTypes: + """Test the pre-defined constrained types.""" + + def test_constrained_types_valid(self) -> None: + """Test that constrained types work correctly with valid values.""" + + class TestModel(BaseModel): + language: LanguageTag + country: CountryCode + region: RegionCode + timestamp: datetime + pointer: JsonPointer + color: HexColor + + model = TestModel( + language="en-US", + country="US", + region="US-CA", + timestamp="2023-10-15T10:30:00Z", + pointer="/foo/bar", + color="#FF0000", + ) + + assert model.language == "en-US" + assert model.country == "US" + assert model.region == "US-CA" + assert model.color == "#FF0000" + + def test_constrained_types_invalid(self) -> None: + """Test that constrained types reject invalid values.""" + + class TestModel(BaseModel): + language: LanguageTag + country: CountryCode + color: HexColor + + # Test invalid language tag + with pytest.raises(ValidationError): + TestModel(language="invalid-tag-format", country="US", color="#FF0000") + + # Test invalid country code + with pytest.raises(ValidationError): + TestModel(language="en-US", country="invalid", color="#FF0000") + + # Test invalid color + with pytest.raises(ValidationError): + TestModel(language="en-US", country="US", color="not-a-color") diff --git a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py index 6e4fc674a..368641b38 100644 --- a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py +++ b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py @@ -14,8 +14,6 @@ ) from overture.schema.core.types import ( ConfidenceScore, - PhoneNumber, - WikidataId, ) from overture.schema.foundation.constraint import ( UniqueItemsConstraint, @@ -25,6 +23,7 @@ GeometryType, GeometryTypeConstraint, ) +from overture.schema.foundation.string import PhoneNumber, WikidataId from ..types import PlaceCategory from .enums import OperatingStatus diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py index 846aa13ea..94e89ec10 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py @@ -15,8 +15,6 @@ Level, LinearlyReferencedPosition, OpeningHours, - StrippedString, - WikidataId, ) from overture.schema.core.validation import ( ConstraintValidatedModel, @@ -25,6 +23,7 @@ ) from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.foundation.primitive import float64, int32 +from overture.schema.foundation.string import StrippedString, WikidataId from .enums import ( AccessType, diff --git a/packages/overture-schema-validation/src/overture/schema/validation/__init__.py b/packages/overture-schema-validation/src/overture/schema/validation/__init__.py index 06f4f47c0..f5d7d9a16 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/__init__.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/__init__.py @@ -32,18 +32,7 @@ class MyModel(BaseModel): from .constraints import ( CategoryPatternConstraint, ConfidenceScoreConstraint, - CountryCodeConstraint, - HexColorConstraint, - JsonPointerConstraint, - LanguageTagConstraint, LinearReferenceRangeConstraint, - NoWhitespaceConstraint, - PatternConstraint, - PhoneNumberConstraint, - RegionCodeConstraint, - StringConstraint, - StrippedConstraint, - WikidataConstraint, ) from .mixin import ( ConstraintValidatedModel, @@ -59,19 +48,7 @@ class MyModel(BaseModel): "CategoryPatternConstraint", "ConfidenceScoreConstraint", "ConstraintValidatedModel", - "CountryCodeConstraint", - "HexColorConstraint", - "JsonPointerConstraint", - "LanguageTagConstraint", "LinearReferenceRangeConstraint", - "NoWhitespaceConstraint", - "PatternConstraint", - "PhoneNumberConstraint", - "RegionCodeConstraint", - "StringConstraint", - "UniqueItemsConstraint", - "StrippedConstraint", - "WikidataConstraint", "allow_extension_fields", "any_of", "exactly_one_of", diff --git a/packages/overture-schema-validation/src/overture/schema/validation/constraints.py b/packages/overture-schema-validation/src/overture/schema/validation/constraints.py index fbc390355..89b4cd344 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/constraints.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/constraints.py @@ -12,204 +12,7 @@ from pydantic_core import InitErrorDetails, core_schema from overture.schema.foundation.constraint import CollectionConstraint, Constraint - - -class StringConstraint(Constraint): - """Base class for string-based constraints.""" - - def __get_pydantic_core_schema__( - self, source: type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.CoreSchema: - python_schema = handler(str) - - def validate_string(value: str, info: ValidationInfo) -> str: - self.validate(value, info) - return value - - return core_schema.with_info_after_validator_function( - validate_string, python_schema - ) - - -class PatternConstraint(StringConstraint): - """Generic pattern-based string constraint.""" - - def __init__(self, pattern: str, error_message: str, flags: int = 0): - self.pattern = re.compile(pattern, flags) - self.pattern_str = pattern - self.error_message = error_message - - def validate(self, value: str, info: ValidationInfo) -> None: - if not self.pattern.match(value): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={"error": self.error_message.format(value=value)}, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = self.pattern_str - return json_schema - - -class LanguageTagConstraint(StringConstraint): - """This pattern recognizes BCP-47 language tags at the lexical or syntactic level. - It verifies that candidate tags follow the grammar described in the RFC, but not - that they are validly registered tag in IANA's language subtag registry. - - In understanding the regular expression, remark that '(:?' indicates a non-capturing - group, and that all the top-level or non-nested groups represent top-level - components of `langtag` referenced in the syntax section of - https://www.rfc-editor.org/rfc/bcp/bcp47.txt. In particular, the top-level groups in - left-to-right order represent: - - 1. language - 2. ["-" script] - 3. ["-" region] - 4. *("-" variant) - 5. *("-" extension) - """ - - def __init__(self) -> None: - self.pattern = re.compile( - r"^(?:(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}?)|(?:[A-Za-z]{4,8}))(?:-[A-Za-z]{4})?(?:-[A-Za-z]{2}|[0-9]{3})?(?:-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(?:-[A-WY-Za-wy-z0-9](?:-[A-Za-z0-9]{2,8})+)*$" - ) - - def validate(self, value: str, info: ValidationInfo) -> None: - if not self.pattern.match(value): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={"error": f"Invalid IETF BCP-47 language tag: {value}"}, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = self.pattern.pattern - json_schema["description"] = "IETF BCP-47 language tag" - return json_schema - - -class CountryCodeConstraint(StringConstraint): - """ISO 3166-1 alpha-2 country code constraint.""" - - def __init__(self) -> None: - self.pattern = re.compile(r"^[A-Z]{2}$") - - def validate(self, value: str, info: ValidationInfo) -> None: - if not self.pattern.match(value): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={ - "error": f"Invalid ISO 3166-1 alpha-2 country code: {value}" - }, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = self.pattern.pattern - json_schema["minLength"] = 2 - json_schema["maxLength"] = 2 - json_schema["description"] = "ISO 3166-1 alpha-2 country code" - return json_schema - - -class RegionCodeConstraint(StringConstraint): - """ISO 3166-2 subdivision code constraint.""" - - def __init__(self) -> None: - self.pattern = re.compile(r"^[A-Z]{2}-[A-Z0-9]{1,3}$") - - def validate(self, value: str, info: ValidationInfo) -> None: - if not self.pattern.match(value): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={"error": f"Invalid ISO 3166-2 subdivision code: {value}"}, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = self.pattern.pattern - json_schema["minLength"] = 4 - json_schema["maxLength"] = 6 - json_schema["description"] = "ISO 3166-2 subdivision code" - return json_schema - - -class JsonPointerConstraint(StringConstraint): - """JSON Pointer constraint (RFC 6901).""" - - def validate(self, value: str, info: ValidationInfo) -> None: - # Empty string represents root pointer - if value == "": - return - - if not value.startswith("/"): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={ - "error": f"JSON Pointer must start with '/' or be empty string: {value}" - }, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["description"] = "JSON Pointer (RFC 6901)" - return json_schema +from overture.schema.foundation.constraint.string import StringConstraint class LinearReferenceRangeConstraint(CollectionConstraint): @@ -282,36 +85,6 @@ def __get_pydantic_json_schema__( return json_schema -class StrippedConstraint(StringConstraint): - """Constraint to ensure string has no leading/trailing whitespace.""" - - def validate(self, value: str, info: ValidationInfo) -> None: - if value != value.strip(): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={ - "error": f"String cannot have leading or trailing whitespace: {repr(value)}" - }, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = r"^(\S.*)?\S$" - json_schema["description"] = "String with no leading/trailing whitespace" - return json_schema - - class CategoryPatternConstraint(StringConstraint): """Constraint for place category patterns (snake_case).""" @@ -345,74 +118,6 @@ def __get_pydantic_json_schema__( return json_schema -class WikidataConstraint(StringConstraint): - """Constraint for Wikidata identifiers (Q followed by digits).""" - - def __init__(self) -> None: - self.pattern = re.compile(r"^Q\d+$") - - def validate(self, value: str, info: ValidationInfo) -> None: - if not self.pattern.match(value): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={ - "error": f"Invalid Wikidata identifier: {value}. Must be Q followed by digits (e.g., Q123)" - }, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = self.pattern.pattern - json_schema["description"] = "Wikidata identifier (Q followed by digits)" - return json_schema - - -class PhoneNumberConstraint(StringConstraint): - """Constraint for international phone numbers.""" - - def __init__(self) -> None: - self.pattern = re.compile(r"^\+\d{1,3}[\s\-\(\)0-9]+$") - - def validate(self, value: str, info: ValidationInfo) -> None: - if not self.pattern.match(value): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={ - "error": f"Invalid phone number format: {value}. Must start with + and country code" - }, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = self.pattern.pattern - json_schema["description"] = ( - "International phone number (+ followed by country code and number)" - ) - return json_schema - - # TODO: Not understanding why this is needed versus just using vanilla annotation. class ConfidenceScoreConstraint(Constraint): """Constraint for confidence/probability scores (0.0 to 1.0).""" @@ -433,72 +138,6 @@ def __get_pydantic_json_schema__( return json_schema -class HexColorConstraint(StringConstraint): - """Constraint for hexadecimal color codes (e.g., #FF0000 or #FFF).""" - - def __init__(self) -> None: - self.pattern = re.compile(r"^#[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$") - - def validate(self, value: str, info: ValidationInfo) -> None: - if not self.pattern.match(value): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={ - "error": f"Invalid hexadecimal color format: {value}. Must be in format #RGB or #RRGGBB (e.g., #FFF or #FF0000)" - }, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = self.pattern.pattern - json_schema["description"] = "Hexadecimal color code in format #RGB or #RRGGBB" - return json_schema - - -class NoWhitespaceConstraint(StringConstraint): - """Constraint for strings that cannot contain whitespace characters.""" - - def __init__(self) -> None: - self.pattern = re.compile(r"^\S+$") - - def validate(self, value: str, info: ValidationInfo) -> None: - if not self.pattern.match(value): - context = info.context or {} - loc = context.get("loc_prefix", ()) + ("value",) - raise ValidationError.from_exception_data( - title=self.__class__.__name__, - line_errors=[ - InitErrorDetails( - type="value_error", - loc=loc, - input=value, - ctx={ - "error": f"String cannot contain whitespace characters: '{value}'" - }, - ) - ], - ) - - def __get_pydantic_json_schema__( - self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler - ) -> dict[str, Any]: - json_schema = handler(core_schema) - json_schema["pattern"] = self.pattern.pattern - json_schema["description"] = "String without whitespace characters" - return json_schema - - class ExtensionPrefixModelConstraint(Constraint): """Constraint for models that allow only ext_* prefixed extra fields. diff --git a/packages/overture-schema-validation/src/overture/schema/validation/types.py b/packages/overture-schema-validation/src/overture/schema/validation/types.py index 8266f0ffe..02d4d3618 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/types.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/types.py @@ -9,54 +9,10 @@ from .constraints import ( CategoryPatternConstraint, ConfidenceScoreConstraint, - CountryCodeConstraint, - HexColorConstraint, - JsonPointerConstraint, - LanguageTagConstraint, - NoWhitespaceConstraint, - PhoneNumberConstraint, - RegionCodeConstraint, - StrippedConstraint, - WikidataConstraint, ) # String types with constraints PlaceCategory = NewType("PlaceCategory", Annotated[str, CategoryPatternConstraint()]) -CountryCode = NewType( - "CountryCode", - Annotated[ - str, - CountryCodeConstraint(), - Field(description="ISO 3166-1 alpha-2 country code"), - ], -) -HexColor = NewType("HexColor", Annotated[str, HexColorConstraint()]) -JsonPointer = NewType("JsonPointer", Annotated[str, JsonPointerConstraint()]) -LanguageTag = NewType("LanguageTag", Annotated[str, LanguageTagConstraint()]) -NoWhitespaceString = NewType( - "NoWhitespaceString", Annotated[str, NoWhitespaceConstraint()] -) -PhoneNumber = NewType("PhoneNumber", Annotated[str, PhoneNumberConstraint()]) -RegionCode = NewType( - "RegionCode", - Annotated[ - str, - RegionCodeConstraint(), - Field(description="ISO 3166-2 principal subdivision code."), - ], -) -StrippedString = NewType("StrippedString", Annotated[str, StrippedConstraint()]) -WikidataId = NewType( - "WikidataId", - Annotated[ - str, - WikidataConstraint(), - Field( - description="A wikidata ID if available, as found on https://www.wikidata.org/." - ), - ], -) - # Numeric types with constraints ConfidenceScore = NewType( "ConfidenceScore", diff --git a/packages/overture-schema-validation/tests/test_constraints.py b/packages/overture-schema-validation/tests/test_constraints.py index d2bf8865b..1a74dfe0f 100644 --- a/packages/overture-schema-validation/tests/test_constraints.py +++ b/packages/overture-schema-validation/tests/test_constraints.py @@ -1,220 +1,16 @@ -"""Comprehensive tests for constraint-based validation in overture-schema-validation -package.""" - -from datetime import datetime from typing import Annotated import pytest -from pydantic import BaseModel, Field, ValidationError +from pydantic import BaseModel, ValidationError -from overture.schema.foundation.constraint import UniqueItemsConstraint from overture.schema.validation import ( CategoryPatternConstraint, ConfidenceScoreConstraint, - CountryCodeConstraint, - HexColorConstraint, - JsonPointerConstraint, - LanguageTagConstraint, LinearReferenceRangeConstraint, - NoWhitespaceConstraint, - PatternConstraint, - PhoneNumberConstraint, - RegionCodeConstraint, - StrippedConstraint, - WikidataConstraint, -) -from overture.schema.validation.types import ( - ConfidenceScore, - CountryCode, - HexColor, - JsonPointer, - LanguageTag, - RegionCode, ) -class TestStringConstraints: - """Test all string-based constraints.""" - - def test_pattern_constraint_valid(self) -> None: - """Test PatternConstraint with valid values.""" - constraint = PatternConstraint(r"^[A-Z]{2}$", "Must be 2 uppercase letters") - - class TestModel(BaseModel): - code: Annotated[str, constraint] - - # Valid values - model = TestModel(code="US") - assert model.code == "US" - - model = TestModel(code="GB") - assert model.code == "GB" - - def test_pattern_constraint_invalid(self) -> None: - """Test PatternConstraint with invalid values.""" - constraint = PatternConstraint(r"^[A-Z]{2}$", "Must be 2 uppercase letters") - - class TestModel(BaseModel): - code: Annotated[str, constraint] - - # Invalid values - with pytest.raises(ValidationError) as exc_info: - TestModel(code="usa") - assert "Must be 2 uppercase letters" in str(exc_info.value) - - with pytest.raises(ValidationError): - TestModel(code="123") - - def test_language_tag_constraint_valid(self) -> None: - """Test LanguageTagConstraint with valid language tags.""" - - class TestModel(BaseModel): - language: Annotated[str, LanguageTagConstraint()] - - # Valid language tags - valid_tags = ["en", "en-US", "en-GB", "zh-CN", "fr-CA", "es-MX"] - - for tag in valid_tags: - model = TestModel(language=tag) - assert model.language == tag - - def test_language_tag_constraint_invalid(self) -> None: - """Test LanguageTagConstraint with invalid language tags.""" - - class TestModel(BaseModel): - language: Annotated[str, LanguageTagConstraint()] - - invalid_tags = ["invalid-tag-format", "123", "en_US", "toolongcode"] - - for tag in invalid_tags: - with pytest.raises(ValidationError) as exc_info: - TestModel(language=tag) - assert "Invalid IETF BCP-47 language tag" in str(exc_info.value) - - def test_country_code_constraint_valid(self) -> None: - """Test CountryCodeConstraint with valid ISO 3166-1 alpha-2 codes.""" - - class TestModel(BaseModel): - country: Annotated[str, CountryCodeConstraint()] - - valid_codes = ["US", "GB", "CA", "FR", "DE", "JP", "CN", "BR"] - - for code in valid_codes: - model = TestModel(country=code) - assert model.country == code - - def test_country_code_constraint_invalid(self) -> None: - """Test CountryCodeConstraint with invalid country codes.""" - - class TestModel(BaseModel): - country: Annotated[str, CountryCodeConstraint()] - - invalid_codes = ["USA", "123", "invalid", "gb", "us"] - - for code in invalid_codes: - with pytest.raises(ValidationError) as exc_info: - TestModel(country=code) - assert "Invalid ISO 3166-1 alpha-2 country code" in str(exc_info.value) - - def test_region_code_constraint_valid(self) -> None: - """Test RegionCodeConstraint with valid ISO 3166-2 codes.""" - - class TestModel(BaseModel): - region: Annotated[str, RegionCodeConstraint()] - - valid_codes = ["US-CA", "GB-ENG", "CA-ON", "FR-75", "DE-BY"] - - for code in valid_codes: - model = TestModel(region=code) - assert model.region == code - - def test_region_code_constraint_invalid(self) -> None: - """Test RegionCodeConstraint with invalid region codes.""" - - class TestModel(BaseModel): - region: Annotated[str, RegionCodeConstraint()] - - invalid_codes = ["US", "123-45", "invalid-region", "us-ca"] - - for code in invalid_codes: - with pytest.raises(ValidationError) as exc_info: - TestModel(region=code) - assert "Invalid ISO 3166-2 subdivision code" in str(exc_info.value) - - def test_json_pointer_constraint_valid(self) -> None: - """Test JsonPointerConstraint with valid JSON pointers.""" - - class TestModel(BaseModel): - pointer: Annotated[str, JsonPointerConstraint()] - - valid_pointers = [ - "", - "/foo", - "/foo/bar", - "/0", - "/foo/0/bar", - "/~0", # Represents ~ - "/~1", # Represents / - ] - - for ptr in valid_pointers: - model = TestModel(pointer=ptr) - assert model.pointer == ptr - - def test_json_pointer_constraint_invalid(self) -> None: - """Test JsonPointerConstraint with invalid JSON pointers.""" - - class TestModel(BaseModel): - pointer: Annotated[str, JsonPointerConstraint()] - - invalid_pointers = [ - "foo", # Must start with / - "foo/bar", # Must start with / - ] - - for ptr in invalid_pointers: - with pytest.raises(ValidationError) as exc_info: - TestModel(pointer=ptr) - assert "JSON Pointer must start" in str(exc_info.value) - - def test_whitespace_constraint_valid(self) -> None: - """Test WhitespaceConstraint with valid strings (no leading/trailing - whitespace).""" - - class TestModel(BaseModel): - text: Annotated[str, StrippedConstraint()] - - valid_strings = [ - "hello", - "hello world", - "text with internal spaces", - "", # Empty string is valid - ] - - for text in valid_strings: - model = TestModel(text=text) - assert model.text == text - - def test_whitespace_constraint_invalid(self) -> None: - """Test WhitespaceConstraint with invalid strings (leading/trailing - whitespace).""" - - class TestModel(BaseModel): - text: Annotated[str, StrippedConstraint()] - - invalid_strings = [ - " hello", # Leading space - "hello ", # Trailing space - "\thello", # Leading tab - "hello\n", # Trailing newline - " hello world ", # Both leading and trailing - ] - - for text in invalid_strings: - with pytest.raises(ValidationError) as exc_info: - TestModel(text=text) - assert "cannot have leading or trailing whitespace" in str(exc_info.value) - +class TestCategoryPatternConstraint: def test_category_pattern_constraint_valid(self) -> None: """Test CategoryPatternConstraint with valid snake_case patterns.""" @@ -251,196 +47,6 @@ class TestModel(BaseModel): TestModel(category=cat) assert "Invalid category format" in str(exc_info.value) - def test_wikidata_constraint_valid(self) -> None: - """Test WikidataConstraint with valid Wikidata identifiers.""" - - class TestModel(BaseModel): - wikidata_id: Annotated[str, WikidataConstraint()] - - valid_ids = ["Q1", "Q123", "Q999999", "Q1234567890"] - - for wid in valid_ids: - model = TestModel(wikidata_id=wid) - assert model.wikidata_id == wid - - def test_wikidata_constraint_invalid(self) -> None: - """Test WikidataConstraint with invalid Wikidata identifiers.""" - - class TestModel(BaseModel): - wikidata_id: Annotated[str, WikidataConstraint()] - - invalid_ids = [ - "q123", # Lowercase q - "P123", # Property instead of item - "Q", # Missing number - "123", # Missing Q prefix - "Q12abc", # Non-numeric suffix - ] - - for wid in invalid_ids: - with pytest.raises(ValidationError) as exc_info: - TestModel(wikidata_id=wid) - assert "Invalid Wikidata identifier" in str(exc_info.value) - - def test_phone_number_constraint_valid(self) -> None: - """Test PhoneNumberConstraint with valid international phone numbers.""" - - class TestModel(BaseModel): - phone: Annotated[str, PhoneNumberConstraint()] - - valid_phones = [ - "+1-555-123-4567", - "+44-20-7946-0958", - "+33-1-42-86-83-26", - "+81-3-1234-5678", - "+86-10-8888-8888", - ] - - for phone in valid_phones: - model = TestModel(phone=phone) - assert model.phone == phone - - def test_phone_number_constraint_invalid(self) -> None: - """Test PhoneNumberConstraint with invalid phone numbers.""" - - class TestModel(BaseModel): - phone: Annotated[str, PhoneNumberConstraint()] - - invalid_phones = [ - "555-123-4567", # Missing country code - "1-555-123-4567", # Missing + - "not-a-phone", # Not a phone number - ] - - for phone in invalid_phones: - with pytest.raises(ValidationError) as exc_info: - TestModel(phone=phone) - assert "Invalid phone number format" in str(exc_info.value) - - def test_hex_color_constraint_valid(self) -> None: - """Test HexColorConstraint with valid hex colors.""" - - class TestModel(BaseModel): - color: Annotated[str, HexColorConstraint()] - - valid_colors = [ - "#FFFFFF", - "#000000", - "#FF0000", - "#00FF00", - "#0000FF", - "#ABCDEF", - "#123456", - "#ffffff", # lowercase - "#abcdef", # lowercase - "#FFF", # 3-character uppercase - "#fff", # 3-character lowercase - "#ABC", # 3-character mixed case - "#123", # 3-character numbers - ] - - for color in valid_colors: - model = TestModel(color=color) - assert model.color == color - - def test_hex_color_constraint_invalid(self) -> None: - """Test HexColorConstraint with invalid hex colors.""" - - class TestModel(BaseModel): - color: Annotated[str, HexColorConstraint()] - - invalid_colors = [ - "FFFFFF", # Missing # - "#FF", # Too short (2 chars) - "#FFFFFFF", # Too long (7 chars) - "#GGGGGG", # Invalid hex characters - "red", # Not hex - "#", # Just hash - "#FFFF", # Invalid length (4 chars) - ] - - for color in invalid_colors: - with pytest.raises(ValidationError) as exc_info: - TestModel(color=color) - # Just check that validation fails - message may vary - assert len(exc_info.value.errors()) > 0 - - def test_no_whitespace_constraint_valid(self) -> None: - """Test NoWhitespaceConstraint with valid strings (no whitespace).""" - - class TestModel(BaseModel): - identifier: Annotated[str, NoWhitespaceConstraint()] - - valid_identifiers = [ - "hello", - "identifier123", - "snake_case_id", - "kebab-case-id", - "camelCaseId", - ] - - for ident in valid_identifiers: - model = TestModel(identifier=ident) - assert model.identifier == ident - - def test_no_whitespace_constraint_invalid(self) -> None: - """Test NoWhitespaceConstraint with invalid strings (containing whitespace).""" - - class TestModel(BaseModel): - identifier: Annotated[str, NoWhitespaceConstraint()] - - invalid_identifiers = [ - "hello world", - "id with spaces", - "tab\tcharacter", - "new\nline", - "carriage\rreturn", - ] - - for ident in invalid_identifiers: - with pytest.raises(ValidationError) as exc_info: - TestModel(identifier=ident) - # Just check that validation fails - message may vary - assert len(exc_info.value.errors()) > 0 - - -class TestCollectionConstraints: - """Test all collection-based constraints.""" - - def test_unique_items_constraint_valid(self) -> None: - """Test UniqueItemsConstraint with unique items.""" - - class TestModel(BaseModel): - tags: Annotated[list[str], UniqueItemsConstraint()] - - valid_lists = [ - [], - ["a"], - ["a", "b", "c"], - ["unique", "items", "only"], - ] - - for items in valid_lists: - model = TestModel(tags=items) - assert model.tags == items - - def test_unique_items_constraint_invalid(self) -> None: - """Test UniqueItemsConstraint with duplicate items.""" - - class TestModel(BaseModel): - tags: Annotated[list[str], UniqueItemsConstraint()] - - invalid_lists = [ - ["a", "a"], - ["a", "b", "a"], - ["duplicate", "values", "duplicate"], - ] - - for items in invalid_lists: - with pytest.raises(ValidationError) as exc_info: - TestModel(tags=items) - assert "All items must be unique" in str(exc_info.value) - class TestNumericConstraints: """Test all numeric constraints.""" @@ -514,143 +120,3 @@ class TestModel(BaseModel): TestModel(range_val=range_val) # Check that validation fails with appropriate error assert len(exc_info.value.errors()) > 0 - - -class TestConstrainedTypes: - """Test the pre-defined constrained types.""" - - def test_constrained_types_valid(self) -> None: - """Test that constrained types work correctly with valid values.""" - - class TestModel(BaseModel): - language: LanguageTag - country: CountryCode - region: RegionCode - timestamp: datetime - pointer: JsonPointer - confidence: ConfidenceScore - color: HexColor - - model = TestModel( - language="en-US", - country="US", - region="US-CA", - timestamp="2023-10-15T10:30:00Z", - pointer="/foo/bar", - confidence=0.95, - color="#FF0000", - ) - - assert model.language == "en-US" - assert model.country == "US" - assert model.region == "US-CA" - assert model.confidence == 0.95 - assert model.color == "#FF0000" - - def test_constrained_types_invalid(self) -> None: - """Test that constrained types reject invalid values.""" - - class TestModel(BaseModel): - language: LanguageTag - country: CountryCode - color: HexColor - - # Test invalid language tag - with pytest.raises(ValidationError): - TestModel(language="invalid-tag-format", country="US", color="#FF0000") - - # Test invalid country code - with pytest.raises(ValidationError): - TestModel(language="en-US", country="invalid", color="#FF0000") - - # Test invalid color - with pytest.raises(ValidationError): - TestModel(language="en-US", country="US", color="not-a-color") - - -class TestJSONSchemaGeneration: - """Test JSON schema generation for all constraints.""" - - def test_string_constraints_json_schema(self) -> None: - """Test that string constraints generate proper JSON schema.""" - - class TestModel(BaseModel): - language: Annotated[str, LanguageTagConstraint()] - country: Annotated[str, CountryCodeConstraint()] - color: Annotated[str, HexColorConstraint()] - - schema = TestModel.model_json_schema() - props = schema["properties"] - - # Check that patterns are included - assert "pattern" in props["language"] - assert "pattern" in props["country"] - assert "pattern" in props["color"] - - # Check descriptions - assert "IETF BCP-47 language tag" in props["language"].get("description", "") - - def test_collection_constraints_json_schema(self) -> None: - """Test that collection constraints generate proper JSON schema.""" - - class TestModel(BaseModel): - unique_items: Annotated[list[str], UniqueItemsConstraint()] - min_items: Annotated[list[str], Field(min_length=2)] - max_items: Annotated[list[str], Field(max_length=5)] - - schema = TestModel.model_json_schema() - props = schema["properties"] - - # Check collection constraints - assert props["unique_items"].get("uniqueItems") is True - assert props["min_items"].get("minItems") == 2 - assert props["max_items"].get("maxItems") == 5 - - -class TestErrorHandling: - """Test error handling and validation context.""" - - def test_validation_error_context(self) -> None: - """Test that validation errors include proper context and location info.""" - - class TestModel(BaseModel): - country: Annotated[str, CountryCodeConstraint()] - - with pytest.raises(ValidationError) as exc_info: - TestModel(country="invalid") - - error = exc_info.value - assert error.error_count() == 1 - - error_detail = error.errors()[0] - assert error_detail["type"] == "value_error" - assert error_detail["input"] == "invalid" - assert "Invalid ISO 3166-1 alpha-2 country code" in str( - error_detail["ctx"]["error"] - ) - - def test_nested_validation_errors(self) -> None: - """Test validation errors in nested structures.""" - - class NestedModel(BaseModel): - country: Annotated[str, CountryCodeConstraint()] - - class TestModel(BaseModel): - nested: NestedModel - items: list[NestedModel] - - # Test nested object error - with pytest.raises(ValidationError) as exc_info: - TestModel(nested=NestedModel(country="invalid"), items=[]) - - error = exc_info.value - assert error.error_count() >= 1 - - # Test nested array error - with pytest.raises(ValidationError) as exc_info: - TestModel( - nested=NestedModel(country="US"), items=[NestedModel(country="invalid")] - ) - - error = exc_info.value - assert error.error_count() >= 1 diff --git a/packages/overture-schema-validation/tests/test_json_schema_generation.py b/packages/overture-schema-validation/tests/test_json_schema_generation.py index a27ef69ad..694965e2c 100644 --- a/packages/overture-schema-validation/tests/test_json_schema_generation.py +++ b/packages/overture-schema-validation/tests/test_json_schema_generation.py @@ -7,18 +7,20 @@ from pydantic import BaseModel, Field from overture.schema.foundation.constraint import UniqueItemsConstraint -from overture.schema.validation.constraints import ( - ConfidenceScoreConstraint, +from overture.schema.foundation.constraint.string import ( CountryCodeConstraint, HexColorConstraint, JsonPointerConstraint, LanguageTagConstraint, - LinearReferenceRangeConstraint, NoWhitespaceConstraint, PatternConstraint, RegionCodeConstraint, StrippedConstraint, ) +from overture.schema.validation.constraints import ( + ConfidenceScoreConstraint, + LinearReferenceRangeConstraint, +) from overture.schema.validation.mixin import ( ConstraintValidatedModel, any_of, From b154fa09a118664a0392cf9ab3b4f4029ecb8908 Mon Sep 17 00:00:00 2001 From: schapper Date: Wed, 1 Oct 2025 15:38:06 -0700 Subject: [PATCH 10/20] wip - Add TypeAlias to quiet Pylance warnings --- .../schema/foundation/primitive/num.py | 22 +++++------ .../src/overture/schema/foundation/string.py | 38 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py index 7d183faa7..42ae1ffef 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py @@ -1,8 +1,8 @@ -from typing import Annotated, NewType +from typing import Annotated, NewType, TypeAlias from pydantic import Field -uint8 = NewType("uint8", Annotated[int, Field(ge=0, le=255)]) # type: ignore [type-arg] +uint8: TypeAlias = NewType("uint8", Annotated[int, Field(ge=0, le=255)]) # type: ignore [type-arg] uint8.__doc__ = """ uint8 : NewType Portable 8-bit unsigned integer. @@ -11,7 +11,7 @@ different serialization and validation platforms. """ -uint16 = NewType("uint16", Annotated[int, Field(ge=0, le=65535)]) # type: ignore[type-arg] +uint16: TypeAlias = NewType("uint16", Annotated[int, Field(ge=0, le=65535)]) # type: ignore[type-arg] uint16.__doc__ = """ uint16 : NewType Portable 16-bit unsigned integer. @@ -20,7 +20,7 @@ different serialization and validation platforms. """ -uint32 = NewType("uint32", Annotated[int, Field(ge=0, le=4294967295)]) # type: ignore[type-arg] +uint32: TypeAlias = NewType("uint32", Annotated[int, Field(ge=0, le=4294967295)]) # type: ignore[type-arg] uint32.__doc__ = """ uint32 : NewType Portable 32-bit unsigned integer. @@ -29,7 +29,7 @@ different serialization and validation platforms. """ -int8 = NewType("int8", Annotated[int, Field(ge=-128, le=127)]) # type: ignore[type-arg] +int8: TypeAlias = NewType("int8", Annotated[int, Field(ge=-128, le=127)]) # type: ignore[type-arg] int8.__doc__ = """ int8 : NewType Portable 8-bit signed integer. @@ -38,7 +38,7 @@ different serialization and validation platforms. """ -int16 = NewType("int16", Annotated[int, Field(ge=-32768, le=32767)]) # type: ignore[type-arg] +int16: TypeAlias = NewType("int16", Annotated[int, Field(ge=-32768, le=32767)]) # type: ignore[type-arg] int16.__doc__ = """ int16 : NewType Portable 16-bit signed integer. @@ -47,7 +47,7 @@ different serialization and validation platforms. """ -int32 = NewType("int32", Annotated[int, Field(ge=-(2**31), le=2**31 - 1)]) # type: ignore[type-arg] +int32: TypeAlias = NewType("int32", Annotated[int, Field(ge=-(2**31), le=2**31 - 1)]) # type: ignore[type-arg] int32.__doc__ = """ int32 : NewType Portable 32-bit signed integer. @@ -56,7 +56,7 @@ different serialization and validation platforms. """ -int64 = NewType("int64", Annotated[int, Field(ge=-(2**63), le=2**63 - 1)]) # type: ignore[type-arg] +int64: TypeAlias = NewType("int64", Annotated[int, Field(ge=-(2**63), le=2**63 - 1)]) # type: ignore[type-arg] int64.__doc__ = """ int64 : NewType Portable 64-bit signed integer. @@ -65,7 +65,7 @@ different serialization and validation platforms. """ -float32 = NewType("float32", float) +float32: TypeAlias = NewType("float32", float) # type: ignore[type-arg] float32.__doc__ = """ float32 : NewType Portable IEEE 32-bit floating point number. @@ -74,7 +74,7 @@ across different serialization and validation platforms. """ -float64 = NewType("float64", float) +float64: TypeAlias = NewType("float64", float) # type: ignore[type-arg] float64.__doc__ = """ float64 : NewType Portable IEEE 64-bit floating point number. @@ -83,7 +83,7 @@ across different serialization and validation platforms. """ -pct = NewType("pct", Annotated[float, Field(ge=0, le=1)]) +pct: TypeAlias = NewType("pct", Annotated[float, Field(ge=0, le=1)]) # type: ignore[type-arg] pct.__doc__ = """ pct : NewType Portable percent value in the range [0, 1] where 0 represents 0% and 1 represents 100%. diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/string.py b/packages/overture-schema-foundation/src/overture/schema/foundation/string.py index 637481726..1b10c3484 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/string.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/string.py @@ -1,4 +1,4 @@ -from typing import Annotated, NewType +from typing import Annotated, NewType, TypeAlias from pydantic import Field @@ -14,7 +14,7 @@ WikidataIdConstraint, ) -HexColor = NewType( +HexColor: TypeAlias = NewType( "HexColor", Annotated[ str, @@ -23,7 +23,7 @@ description="A color represented as an #RRGGBB or #RGB hexadecimal string, for example #ff0000 for pure red." ), ], -) +) # type: ignore [type-arg] HexColor.__doc__ = """ HexColor : NewType A color represented as an #RRGGBB or #RGB hexadecimal string. @@ -36,14 +36,14 @@ """ -JsonPointer = NewType( +JsonPointer: TypeAlias = NewType( "JsonPointer", Annotated[ str, JsonPointerConstraint(), Field(description="A JSON Pointer (as described in RFC-6901)"), ], -) +) # type: ignore [type-arg] JsonPointer.__doc__ = """ JsonPointer : NewType A JSON Pointer @@ -52,7 +52,7 @@ """ -LanguageTag = NewType( +LanguageTag: TypeAlias = NewType( "LanguageTag", Annotated[ str, @@ -61,7 +61,7 @@ description="A BCP-47 language tag", ), ], -) +) # type: ignore [type-arg] LanguageTag.__doc__ = """ LanguageTag : NewType A BCP-47 language tag. @@ -70,7 +70,7 @@ """ -StrippedString = NewType( +StrippedString: TypeAlias = NewType( "StrippedString", Annotated[ str, @@ -79,73 +79,73 @@ description="A string without leading or trailing whitespace", ), ], -) +) # type: ignore [type-arg] StrippedString.__doc__ = """ StrippedString : NewType A string without leading or trailing whitespace. """ -NoWhitespaceString = NewType( +NoWhitespaceString: TypeAlias = NewType( "NoWhitespaceString", Annotated[ str, NoWhitespaceConstraint(), Field(description="A string that contains no whitespace characters"), ], -) +) # type: ignore [type-arg] NoWhitespaceString.__doc__ = """ NoWhitespaceString : NewType A string that contains no whitespace characters. """ -CountryCode = NewType( +CountryCode: TypeAlias = NewType( "CountryCode", Annotated[ str, CountryCodeConstraint(), Field(description="An ISO 3166-1 alpha-2 country code"), ], -) +) # type: ignore [type-arg] CountryCode.__doc__ = """ CountryCode : NewType An ISO-316601 alpha-2 country code. """ -RegionCode = NewType( +RegionCode: TypeAlias = NewType( "RegionCode", Annotated[ str, RegionCodeConstraint(), Field(description="An ISO 3166-2 principal subdivision code"), ], -) +) # type: ignore [type-arg] RegionCode.__doc__ = """ RegionCode : NewType An ISO 3166-2 principal subdivision code. """ -WikidataId = NewType( +WikidataId: TypeAlias = NewType( "WikidataId", Annotated[ str, WikidataIdConstraint(), Field(description="A wikidata ID, as found on https://www.wikidata.org/"), ], -) +) # type: ignore [type-arg] WikidataId.__doc__ = """ WikidataId : NewType A wikidata ID, as found on https://www.wikidata.org/. """ -PhoneNumber = NewType( +PhoneNumber: TypeAlias = NewType( "PhoneNumber", Annotated[ str, PhoneNumberConstraint(), Field(description="An international phone number") ], -) +) # type: ignore [type-arg] PhoneNumber.__doc__ = """ PhoneNumber : NewType An international telephone number. From 76c617a81e6a2d8ab7317c69d83083e087d514b5 Mon Sep 17 00:00:00 2001 From: schapper Date: Thu, 2 Oct 2025 14:30:11 -0700 Subject: [PATCH 11/20] wip - add unmodeled dependencies --- pyproject.toml | 6 +- uv.lock | 1196 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1199 insertions(+), 3 deletions(-) create mode 100644 uv.lock diff --git a/pyproject.toml b/pyproject.toml index fc761008b..9d86cce3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,10 +48,10 @@ warn_unused_configs = true [dependency-groups] dev = [ "docformatter>=1.7.7", - "mypy", - "pytest>=7.0", + "mypy>=1.17.0", + "pytest>=8.4.1", "pytest-cov", - "ruff", + "ruff>=0.12.4", ] [tool.docformatter] diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..e294d320f --- /dev/null +++ b/uv.lock @@ -0,0 +1,1196 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version < '3.11'", +] + +[manifest] +members = [ + "overture-schema", + "overture-schema-addresses-theme", + "overture-schema-base-theme", + "overture-schema-buildings-theme", + "overture-schema-core", + "overture-schema-divisions-theme", + "overture-schema-foundation", + "overture-schema-places-theme", + "overture-schema-transportation-theme", + "overture-schema-validation", + "overture-schema-workspace", +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/b7/c0465ca253df10a9e8dae0692a4ae6e9726d245390aaef92360e1d6d3832/coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b", size = 813556, upload-time = "2025-07-03T10:54:15.101Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/0d/5c2114fd776c207bd55068ae8dc1bef63ecd1b767b3389984a8e58f2b926/coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912", size = 212039, upload-time = "2025-07-03T10:52:38.955Z" }, + { url = "https://files.pythonhosted.org/packages/cf/ad/dc51f40492dc2d5fcd31bb44577bc0cc8920757d6bc5d3e4293146524ef9/coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f", size = 212428, upload-time = "2025-07-03T10:52:41.36Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a3/55cb3ff1b36f00df04439c3993d8529193cdf165a2467bf1402539070f16/coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f", size = 241534, upload-time = "2025-07-03T10:52:42.956Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c9/a8410b91b6be4f6e9c2e9f0dce93749b6b40b751d7065b4410bf89cb654b/coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf", size = 239408, upload-time = "2025-07-03T10:52:44.199Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c4/6f3e56d467c612b9070ae71d5d3b114c0b899b5788e1ca3c93068ccb7018/coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547", size = 240552, upload-time = "2025-07-03T10:52:45.477Z" }, + { url = "https://files.pythonhosted.org/packages/fd/20/04eda789d15af1ce79bce5cc5fd64057c3a0ac08fd0576377a3096c24663/coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45", size = 240464, upload-time = "2025-07-03T10:52:46.809Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5a/217b32c94cc1a0b90f253514815332d08ec0812194a1ce9cca97dda1cd20/coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2", size = 239134, upload-time = "2025-07-03T10:52:48.149Z" }, + { url = "https://files.pythonhosted.org/packages/34/73/1d019c48f413465eb5d3b6898b6279e87141c80049f7dbf73fd020138549/coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e", size = 239405, upload-time = "2025-07-03T10:52:49.687Z" }, + { url = "https://files.pythonhosted.org/packages/49/6c/a2beca7aa2595dad0c0d3f350382c381c92400efe5261e2631f734a0e3fe/coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e", size = 214519, upload-time = "2025-07-03T10:52:51.036Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c8/91e5e4a21f9a51e2c7cdd86e587ae01a4fcff06fc3fa8cde4d6f7cf68df4/coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c", size = 215400, upload-time = "2025-07-03T10:52:52.313Z" }, + { url = "https://files.pythonhosted.org/packages/39/40/916786453bcfafa4c788abee4ccd6f592b5b5eca0cd61a32a4e5a7ef6e02/coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba", size = 212152, upload-time = "2025-07-03T10:52:53.562Z" }, + { url = "https://files.pythonhosted.org/packages/9f/66/cc13bae303284b546a030762957322bbbff1ee6b6cb8dc70a40f8a78512f/coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa", size = 212540, upload-time = "2025-07-03T10:52:55.196Z" }, + { url = "https://files.pythonhosted.org/packages/0f/3c/d56a764b2e5a3d43257c36af4a62c379df44636817bb5f89265de4bf8bd7/coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a", size = 245097, upload-time = "2025-07-03T10:52:56.509Z" }, + { url = "https://files.pythonhosted.org/packages/b1/46/bd064ea8b3c94eb4ca5d90e34d15b806cba091ffb2b8e89a0d7066c45791/coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc", size = 242812, upload-time = "2025-07-03T10:52:57.842Z" }, + { url = "https://files.pythonhosted.org/packages/43/02/d91992c2b29bc7afb729463bc918ebe5f361be7f1daae93375a5759d1e28/coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2", size = 244617, upload-time = "2025-07-03T10:52:59.239Z" }, + { url = "https://files.pythonhosted.org/packages/b7/4f/8fadff6bf56595a16d2d6e33415841b0163ac660873ed9a4e9046194f779/coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c", size = 244263, upload-time = "2025-07-03T10:53:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d2/e0be7446a2bba11739edb9f9ba4eff30b30d8257370e237418eb44a14d11/coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd", size = 242314, upload-time = "2025-07-03T10:53:01.932Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7d/dcbac9345000121b8b57a3094c2dfcf1ccc52d8a14a40c1d4bc89f936f80/coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74", size = 242904, upload-time = "2025-07-03T10:53:03.478Z" }, + { url = "https://files.pythonhosted.org/packages/41/58/11e8db0a0c0510cf31bbbdc8caf5d74a358b696302a45948d7c768dfd1cf/coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6", size = 214553, upload-time = "2025-07-03T10:53:05.174Z" }, + { url = "https://files.pythonhosted.org/packages/3a/7d/751794ec8907a15e257136e48dc1021b1f671220ecccfd6c4eaf30802714/coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7", size = 215441, upload-time = "2025-07-03T10:53:06.472Z" }, + { url = "https://files.pythonhosted.org/packages/62/5b/34abcedf7b946c1c9e15b44f326cb5b0da852885312b30e916f674913428/coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62", size = 213873, upload-time = "2025-07-03T10:53:07.699Z" }, + { url = "https://files.pythonhosted.org/packages/53/d7/7deefc6fd4f0f1d4c58051f4004e366afc9e7ab60217ac393f247a1de70a/coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0", size = 212344, upload-time = "2025-07-03T10:53:09.3Z" }, + { url = "https://files.pythonhosted.org/packages/95/0c/ee03c95d32be4d519e6a02e601267769ce2e9a91fc8faa1b540e3626c680/coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3", size = 212580, upload-time = "2025-07-03T10:53:11.52Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9f/826fa4b544b27620086211b87a52ca67592622e1f3af9e0a62c87aea153a/coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1", size = 246383, upload-time = "2025-07-03T10:53:13.134Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b3/4477aafe2a546427b58b9c540665feff874f4db651f4d3cb21b308b3a6d2/coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615", size = 243400, upload-time = "2025-07-03T10:53:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/f8/c2/efffa43778490c226d9d434827702f2dfbc8041d79101a795f11cbb2cf1e/coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b", size = 245591, upload-time = "2025-07-03T10:53:15.872Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e7/a59888e882c9a5f0192d8627a30ae57910d5d449c80229b55e7643c078c4/coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9", size = 245402, upload-time = "2025-07-03T10:53:17.124Z" }, + { url = "https://files.pythonhosted.org/packages/92/a5/72fcd653ae3d214927edc100ce67440ed8a0a1e3576b8d5e6d066ed239db/coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f", size = 243583, upload-time = "2025-07-03T10:53:18.781Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f5/84e70e4df28f4a131d580d7d510aa1ffd95037293da66fd20d446090a13b/coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d", size = 244815, upload-time = "2025-07-03T10:53:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/39/e7/d73d7cbdbd09fdcf4642655ae843ad403d9cbda55d725721965f3580a314/coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355", size = 214719, upload-time = "2025-07-03T10:53:21.521Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d6/7486dcc3474e2e6ad26a2af2db7e7c162ccd889c4c68fa14ea8ec189c9e9/coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0", size = 215509, upload-time = "2025-07-03T10:53:22.853Z" }, + { url = "https://files.pythonhosted.org/packages/b7/34/0439f1ae2593b0346164d907cdf96a529b40b7721a45fdcf8b03c95fcd90/coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b", size = 213910, upload-time = "2025-07-03T10:53:24.472Z" }, + { url = "https://files.pythonhosted.org/packages/94/9d/7a8edf7acbcaa5e5c489a646226bed9591ee1c5e6a84733c0140e9ce1ae1/coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038", size = 212367, upload-time = "2025-07-03T10:53:25.811Z" }, + { url = "https://files.pythonhosted.org/packages/e8/9e/5cd6f130150712301f7e40fb5865c1bc27b97689ec57297e568d972eec3c/coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d", size = 212632, upload-time = "2025-07-03T10:53:27.075Z" }, + { url = "https://files.pythonhosted.org/packages/a8/de/6287a2c2036f9fd991c61cefa8c64e57390e30c894ad3aa52fac4c1e14a8/coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3", size = 245793, upload-time = "2025-07-03T10:53:28.408Z" }, + { url = "https://files.pythonhosted.org/packages/06/cc/9b5a9961d8160e3cb0b558c71f8051fe08aa2dd4b502ee937225da564ed1/coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14", size = 243006, upload-time = "2025-07-03T10:53:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6", size = 244990, upload-time = "2025-07-03T10:53:31.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/83/801cdc10f137b2d02b005a761661649ffa60eb173dcdaeb77f571e4dc192/coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b", size = 245157, upload-time = "2025-07-03T10:53:32.717Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a4/41911ed7e9d3ceb0ffb019e7635468df7499f5cc3edca5f7dfc078e9c5ec/coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d", size = 243128, upload-time = "2025-07-03T10:53:34.009Z" }, + { url = "https://files.pythonhosted.org/packages/10/41/344543b71d31ac9cb00a664d5d0c9ef134a0fe87cb7d8430003b20fa0b7d/coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868", size = 244511, upload-time = "2025-07-03T10:53:35.434Z" }, + { url = "https://files.pythonhosted.org/packages/d5/81/3b68c77e4812105e2a060f6946ba9e6f898ddcdc0d2bfc8b4b152a9ae522/coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a", size = 214765, upload-time = "2025-07-03T10:53:36.787Z" }, + { url = "https://files.pythonhosted.org/packages/06/a2/7fac400f6a346bb1a4004eb2a76fbff0e242cd48926a2ce37a22a6a1d917/coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b", size = 215536, upload-time = "2025-07-03T10:53:38.188Z" }, + { url = "https://files.pythonhosted.org/packages/08/47/2c6c215452b4f90d87017e61ea0fd9e0486bb734cb515e3de56e2c32075f/coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694", size = 213943, upload-time = "2025-07-03T10:53:39.492Z" }, + { url = "https://files.pythonhosted.org/packages/a3/46/e211e942b22d6af5e0f323faa8a9bc7c447a1cf1923b64c47523f36ed488/coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5", size = 213088, upload-time = "2025-07-03T10:53:40.874Z" }, + { url = "https://files.pythonhosted.org/packages/d2/2f/762551f97e124442eccd907bf8b0de54348635b8866a73567eb4e6417acf/coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b", size = 213298, upload-time = "2025-07-03T10:53:42.218Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b7/76d2d132b7baf7360ed69be0bcab968f151fa31abe6d067f0384439d9edb/coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3", size = 256541, upload-time = "2025-07-03T10:53:43.823Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/392b219837d7ad47d8e5974ce5f8dc3deb9f99a53b3bd4d123602f960c81/coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8", size = 252761, upload-time = "2025-07-03T10:53:45.19Z" }, + { url = "https://files.pythonhosted.org/packages/d5/77/4256d3577fe1b0daa8d3836a1ebe68eaa07dd2cbaf20cf5ab1115d6949d4/coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46", size = 254917, upload-time = "2025-07-03T10:53:46.931Z" }, + { url = "https://files.pythonhosted.org/packages/53/99/fc1a008eef1805e1ddb123cf17af864743354479ea5129a8f838c433cc2c/coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584", size = 256147, upload-time = "2025-07-03T10:53:48.289Z" }, + { url = "https://files.pythonhosted.org/packages/92/c0/f63bf667e18b7f88c2bdb3160870e277c4874ced87e21426128d70aa741f/coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e", size = 254261, upload-time = "2025-07-03T10:53:49.99Z" }, + { url = "https://files.pythonhosted.org/packages/8c/32/37dd1c42ce3016ff8ec9e4b607650d2e34845c0585d3518b2a93b4830c1a/coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac", size = 255099, upload-time = "2025-07-03T10:53:51.354Z" }, + { url = "https://files.pythonhosted.org/packages/da/2e/af6b86f7c95441ce82f035b3affe1cd147f727bbd92f563be35e2d585683/coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926", size = 215440, upload-time = "2025-07-03T10:53:52.808Z" }, + { url = "https://files.pythonhosted.org/packages/4d/bb/8a785d91b308867f6b2e36e41c569b367c00b70c17f54b13ac29bcd2d8c8/coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd", size = 216537, upload-time = "2025-07-03T10:53:54.273Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a0/a6bffb5e0f41a47279fd45a8f3155bf193f77990ae1c30f9c224b61cacb0/coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb", size = 214398, upload-time = "2025-07-03T10:53:56.715Z" }, + { url = "https://files.pythonhosted.org/packages/d7/85/f8bbefac27d286386961c25515431482a425967e23d3698b75a250872924/coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050", size = 204013, upload-time = "2025-07-03T10:54:12.084Z" }, + { url = "https://files.pythonhosted.org/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "deepdiff" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "orderly-set" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/0f/9cd2624f7dcd755cbf1fa21fb7234541f19a1be96a56f387ec9053ebe220/deepdiff-8.5.0.tar.gz", hash = "sha256:a4dd3529fa8d4cd5b9cbb6e3ea9c95997eaa919ba37dac3966c1b8f872dc1cd1", size = 538517, upload-time = "2025-05-09T18:44:10.035Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/3b/2e0797200c51531a6d8c97a8e4c9fa6fb56de7e6e2a15c1c067b6b10a0b0/deepdiff-8.5.0-py3-none-any.whl", hash = "sha256:d4599db637f36a1c285f5fdfc2cd8d38bde8d8be8636b65ab5e425b67c54df26", size = 85112, upload-time = "2025-05-09T18:44:07.784Z" }, +] + +[[package]] +name = "dnspython" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, +] + +[[package]] +name = "docformatter" +version = "1.7.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "charset-normalizer" }, + { name = "untokenize" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/7b/ee08cb5fe2627ed0b6f0cc4a1c6be6c9c71de5a3e9785de8174273fc3128/docformatter-1.7.7.tar.gz", hash = "sha256:ea0e1e8867e5af468dfc3f9e947b92230a55be9ec17cd1609556387bffac7978", size = 26587, upload-time = "2025-05-11T04:54:04.356Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl", hash = "sha256:7af49f8a46346a77858f6651f431b882c503c2f4442c8b4524b920c863277834", size = 33525, upload-time = "2025-05-11T04:54:03.353Z" }, +] + +[[package]] +name = "email-validator" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967, upload-time = "2024-06-20T11:30:30.034Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521, upload-time = "2024-06-20T11:30:28.248Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "jsonpath-ng" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ply" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/86/08646239a313f895186ff0a4573452038eed8c86f54380b3ebac34d32fb2/jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c", size = 37838, upload-time = "2024-10-11T15:41:42.404Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/5a/73ecb3d82f8615f32ccdadeb9356726d6cae3a4bbc840b437ceb95708063/jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6", size = 30105, upload-time = "2024-11-20T17:58:30.418Z" }, +] + +[[package]] +name = "mypy" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" }, + { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" }, + { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" }, + { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" }, + { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, + { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, + { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, + { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, + { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, + { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, + { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, + { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, + { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, + { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, + { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, + { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, + { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, + { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, + { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, + { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, + { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/37/7d/3fec4199c5ffb892bed55cff901e4f39a58c81df9c44c280499e92cad264/numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48", size = 20489306, upload-time = "2025-07-24T21:32:07.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/26/1320083986108998bd487e2931eed2aeedf914b6e8905431487543ec911d/numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9", size = 21259016, upload-time = "2025-07-24T20:24:35.214Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2b/792b341463fa93fc7e55abbdbe87dac316c5b8cb5e94fb7a59fb6fa0cda5/numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168", size = 14451158, upload-time = "2025-07-24T20:24:58.397Z" }, + { url = "https://files.pythonhosted.org/packages/b7/13/e792d7209261afb0c9f4759ffef6135b35c77c6349a151f488f531d13595/numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b", size = 5379817, upload-time = "2025-07-24T20:25:07.746Z" }, + { url = "https://files.pythonhosted.org/packages/49/ce/055274fcba4107c022b2113a213c7287346563f48d62e8d2a5176ad93217/numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8", size = 6913606, upload-time = "2025-07-24T20:25:18.84Z" }, + { url = "https://files.pythonhosted.org/packages/17/f2/e4d72e6bc5ff01e2ab613dc198d560714971900c03674b41947e38606502/numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d", size = 14589652, upload-time = "2025-07-24T20:25:40.356Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b0/fbeee3000a51ebf7222016e2939b5c5ecf8000a19555d04a18f1e02521b8/numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3", size = 16938816, upload-time = "2025-07-24T20:26:05.721Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ec/2f6c45c3484cc159621ea8fc000ac5a86f1575f090cac78ac27193ce82cd/numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f", size = 16370512, upload-time = "2025-07-24T20:26:30.545Z" }, + { url = "https://files.pythonhosted.org/packages/b5/01/dd67cf511850bd7aefd6347aaae0956ed415abea741ae107834aae7d6d4e/numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097", size = 18884947, upload-time = "2025-07-24T20:26:58.24Z" }, + { url = "https://files.pythonhosted.org/packages/a7/17/2cf60fd3e6a61d006778735edf67a222787a8c1a7842aed43ef96d777446/numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220", size = 6599494, upload-time = "2025-07-24T20:27:09.786Z" }, + { url = "https://files.pythonhosted.org/packages/d5/03/0eade211c504bda872a594f045f98ddcc6caef2b7c63610946845e304d3f/numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170", size = 13087889, upload-time = "2025-07-24T20:27:29.558Z" }, + { url = "https://files.pythonhosted.org/packages/13/32/2c7979d39dafb2a25087e12310fc7f3b9d3c7d960df4f4bc97955ae0ce1d/numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89", size = 10459560, upload-time = "2025-07-24T20:27:46.803Z" }, + { url = "https://files.pythonhosted.org/packages/00/6d/745dd1c1c5c284d17725e5c802ca4d45cfc6803519d777f087b71c9f4069/numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b", size = 20956420, upload-time = "2025-07-24T20:28:18.002Z" }, + { url = "https://files.pythonhosted.org/packages/bc/96/e7b533ea5740641dd62b07a790af5d9d8fec36000b8e2d0472bd7574105f/numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f", size = 14184660, upload-time = "2025-07-24T20:28:39.522Z" }, + { url = "https://files.pythonhosted.org/packages/2b/53/102c6122db45a62aa20d1b18c9986f67e6b97e0d6fbc1ae13e3e4c84430c/numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0", size = 5113382, upload-time = "2025-07-24T20:28:48.544Z" }, + { url = "https://files.pythonhosted.org/packages/2b/21/376257efcbf63e624250717e82b4fae93d60178f09eb03ed766dbb48ec9c/numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b", size = 6647258, upload-time = "2025-07-24T20:28:59.104Z" }, + { url = "https://files.pythonhosted.org/packages/91/ba/f4ebf257f08affa464fe6036e13f2bf9d4642a40228781dc1235da81be9f/numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370", size = 14281409, upload-time = "2025-07-24T20:40:30.298Z" }, + { url = "https://files.pythonhosted.org/packages/59/ef/f96536f1df42c668cbacb727a8c6da7afc9c05ece6d558927fb1722693e1/numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73", size = 16641317, upload-time = "2025-07-24T20:40:56.625Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a7/af813a7b4f9a42f498dde8a4c6fcbff8100eed00182cc91dbaf095645f38/numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc", size = 16056262, upload-time = "2025-07-24T20:41:20.797Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5d/41c4ef8404caaa7f05ed1cfb06afe16a25895260eacbd29b4d84dff2920b/numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be", size = 18579342, upload-time = "2025-07-24T20:41:50.753Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4f/9950e44c5a11636f4a3af6e825ec23003475cc9a466edb7a759ed3ea63bd/numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036", size = 6320610, upload-time = "2025-07-24T20:42:01.551Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2f/244643a5ce54a94f0a9a2ab578189c061e4a87c002e037b0829dd77293b6/numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f", size = 12786292, upload-time = "2025-07-24T20:42:20.738Z" }, + { url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c0/c6bb172c916b00700ed3bf71cb56175fd1f7dbecebf8353545d0b5519f6c/numpy-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c8d9727f5316a256425892b043736d63e89ed15bbfe6556c5ff4d9d4448ff3b3", size = 20949074, upload-time = "2025-07-24T20:43:07.813Z" }, + { url = "https://files.pythonhosted.org/packages/20/4e/c116466d22acaf4573e58421c956c6076dc526e24a6be0903219775d862e/numpy-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:efc81393f25f14d11c9d161e46e6ee348637c0a1e8a54bf9dedc472a3fae993b", size = 14177311, upload-time = "2025-07-24T20:43:29.335Z" }, + { url = "https://files.pythonhosted.org/packages/78/45/d4698c182895af189c463fc91d70805d455a227261d950e4e0f1310c2550/numpy-2.3.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dd937f088a2df683cbb79dda9a772b62a3e5a8a7e76690612c2737f38c6ef1b6", size = 5106022, upload-time = "2025-07-24T20:43:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/9f/76/3e6880fef4420179309dba72a8c11f6166c431cf6dee54c577af8906f914/numpy-2.3.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:11e58218c0c46c80509186e460d79fbdc9ca1eb8d8aee39d8f2dc768eb781089", size = 6640135, upload-time = "2025-07-24T20:43:49.28Z" }, + { url = "https://files.pythonhosted.org/packages/34/fa/87ff7f25b3c4ce9085a62554460b7db686fef1e0207e8977795c7b7d7ba1/numpy-2.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ad4ebcb683a1f99f4f392cc522ee20a18b2bb12a2c1c42c3d48d5a1adc9d3d2", size = 14278147, upload-time = "2025-07-24T20:44:10.328Z" }, + { url = "https://files.pythonhosted.org/packages/1d/0f/571b2c7a3833ae419fe69ff7b479a78d313581785203cc70a8db90121b9a/numpy-2.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f", size = 16635989, upload-time = "2025-07-24T20:44:34.88Z" }, + { url = "https://files.pythonhosted.org/packages/24/5a/84ae8dca9c9a4c592fe11340b36a86ffa9fd3e40513198daf8a97839345c/numpy-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66459dccc65d8ec98cc7df61307b64bf9e08101f9598755d42d8ae65d9a7a6ee", size = 16053052, upload-time = "2025-07-24T20:44:58.872Z" }, + { url = "https://files.pythonhosted.org/packages/57/7c/e5725d99a9133b9813fcf148d3f858df98511686e853169dbaf63aec6097/numpy-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7af9ed2aa9ec5950daf05bb11abc4076a108bd3c7db9aa7251d5f107079b6a6", size = 18577955, upload-time = "2025-07-24T20:45:26.714Z" }, + { url = "https://files.pythonhosted.org/packages/ae/11/7c546fcf42145f29b71e4d6f429e96d8d68e5a7ba1830b2e68d7418f0bbd/numpy-2.3.2-cp313-cp313-win32.whl", hash = "sha256:906a30249315f9c8e17b085cc5f87d3f369b35fedd0051d4a84686967bdbbd0b", size = 6311843, upload-time = "2025-07-24T20:49:24.444Z" }, + { url = "https://files.pythonhosted.org/packages/aa/6f/a428fd1cb7ed39b4280d057720fed5121b0d7754fd2a9768640160f5517b/numpy-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:c63d95dc9d67b676e9108fe0d2182987ccb0f11933c1e8959f42fa0da8d4fa56", size = 12782876, upload-time = "2025-07-24T20:49:43.227Z" }, + { url = "https://files.pythonhosted.org/packages/65/85/4ea455c9040a12595fb6c43f2c217257c7b52dd0ba332c6a6c1d28b289fe/numpy-2.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:b05a89f2fb84d21235f93de47129dd4f11c16f64c87c33f5e284e6a3a54e43f2", size = 10192786, upload-time = "2025-07-24T20:49:59.443Z" }, + { url = "https://files.pythonhosted.org/packages/80/23/8278f40282d10c3f258ec3ff1b103d4994bcad78b0cba9208317f6bb73da/numpy-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e6ecfeddfa83b02318f4d84acf15fbdbf9ded18e46989a15a8b6995dfbf85ab", size = 21047395, upload-time = "2025-07-24T20:45:58.821Z" }, + { url = "https://files.pythonhosted.org/packages/1f/2d/624f2ce4a5df52628b4ccd16a4f9437b37c35f4f8a50d00e962aae6efd7a/numpy-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:508b0eada3eded10a3b55725b40806a4b855961040180028f52580c4729916a2", size = 14300374, upload-time = "2025-07-24T20:46:20.207Z" }, + { url = "https://files.pythonhosted.org/packages/f6/62/ff1e512cdbb829b80a6bd08318a58698867bca0ca2499d101b4af063ee97/numpy-2.3.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:754d6755d9a7588bdc6ac47dc4ee97867271b17cee39cb87aef079574366db0a", size = 5228864, upload-time = "2025-07-24T20:46:30.58Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8e/74bc18078fff03192d4032cfa99d5a5ca937807136d6f5790ce07ca53515/numpy-2.3.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f66e7d2b2d7712410d3bc5684149040ef5f19856f20277cd17ea83e5006286", size = 6737533, upload-time = "2025-07-24T20:46:46.111Z" }, + { url = "https://files.pythonhosted.org/packages/19/ea/0731efe2c9073ccca5698ef6a8c3667c4cf4eea53fcdcd0b50140aba03bc/numpy-2.3.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6ea4e5a65d5a90c7d286ddff2b87f3f4ad61faa3db8dabe936b34c2275b6f8", size = 14352007, upload-time = "2025-07-24T20:47:07.1Z" }, + { url = "https://files.pythonhosted.org/packages/cf/90/36be0865f16dfed20f4bc7f75235b963d5939707d4b591f086777412ff7b/numpy-2.3.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3ef07ec8cbc8fc9e369c8dcd52019510c12da4de81367d8b20bc692aa07573a", size = 16701914, upload-time = "2025-07-24T20:47:32.459Z" }, + { url = "https://files.pythonhosted.org/packages/94/30/06cd055e24cb6c38e5989a9e747042b4e723535758e6153f11afea88c01b/numpy-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:27c9f90e7481275c7800dc9c24b7cc40ace3fdb970ae4d21eaff983a32f70c91", size = 16132708, upload-time = "2025-07-24T20:47:58.129Z" }, + { url = "https://files.pythonhosted.org/packages/9a/14/ecede608ea73e58267fd7cb78f42341b3b37ba576e778a1a06baffbe585c/numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5", size = 18651678, upload-time = "2025-07-24T20:48:25.402Z" }, + { url = "https://files.pythonhosted.org/packages/40/f3/2fe6066b8d07c3685509bc24d56386534c008b462a488b7f503ba82b8923/numpy-2.3.2-cp313-cp313t-win32.whl", hash = "sha256:c771cfac34a4f2c0de8e8c97312d07d64fd8f8ed45bc9f5726a7e947270152b5", size = 6441832, upload-time = "2025-07-24T20:48:37.181Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ba/0937d66d05204d8f28630c9c60bc3eda68824abde4cf756c4d6aad03b0c6/numpy-2.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:72dbebb2dcc8305c431b2836bcc66af967df91be793d63a24e3d9b741374c450", size = 12927049, upload-time = "2025-07-24T20:48:56.24Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ed/13542dd59c104d5e654dfa2ac282c199ba64846a74c2c4bcdbc3a0f75df1/numpy-2.3.2-cp313-cp313t-win_arm64.whl", hash = "sha256:72c6df2267e926a6d5286b0a6d556ebe49eae261062059317837fda12ddf0c1a", size = 10262935, upload-time = "2025-07-24T20:49:13.136Z" }, + { url = "https://files.pythonhosted.org/packages/c9/7c/7659048aaf498f7611b783e000c7268fcc4dcf0ce21cd10aad7b2e8f9591/numpy-2.3.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:448a66d052d0cf14ce9865d159bfc403282c9bc7bb2a31b03cc18b651eca8b1a", size = 20950906, upload-time = "2025-07-24T20:50:30.346Z" }, + { url = "https://files.pythonhosted.org/packages/80/db/984bea9d4ddf7112a04cfdfb22b1050af5757864cfffe8e09e44b7f11a10/numpy-2.3.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:546aaf78e81b4081b2eba1d105c3b34064783027a06b3ab20b6eba21fb64132b", size = 14185607, upload-time = "2025-07-24T20:50:51.923Z" }, + { url = "https://files.pythonhosted.org/packages/e4/76/b3d6f414f4eca568f469ac112a3b510938d892bc5a6c190cb883af080b77/numpy-2.3.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:87c930d52f45df092f7578889711a0768094debf73cfcde105e2d66954358125", size = 5114110, upload-time = "2025-07-24T20:51:01.041Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d2/6f5e6826abd6bca52392ed88fe44a4b52aacb60567ac3bc86c67834c3a56/numpy-2.3.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:8dc082ea901a62edb8f59713c6a7e28a85daddcb67454c839de57656478f5b19", size = 6642050, upload-time = "2025-07-24T20:51:11.64Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/f12b2ade99199e39c73ad182f103f9d9791f48d885c600c8e05927865baf/numpy-2.3.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af58de8745f7fa9ca1c0c7c943616c6fe28e75d0c81f5c295810e3c83b5be92f", size = 14296292, upload-time = "2025-07-24T20:51:33.488Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f9/77c07d94bf110a916b17210fac38680ed8734c236bfed9982fd8524a7b47/numpy-2.3.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed5527c4cf10f16c6d0b6bee1f89958bccb0ad2522c8cadc2efd318bcd545f5", size = 16638913, upload-time = "2025-07-24T20:51:58.517Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d1/9d9f2c8ea399cc05cfff8a7437453bd4e7d894373a93cdc46361bbb49a7d/numpy-2.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:095737ed986e00393ec18ec0b21b47c22889ae4b0cd2d5e88342e08b01141f58", size = 16071180, upload-time = "2025-07-24T20:52:22.827Z" }, + { url = "https://files.pythonhosted.org/packages/4c/41/82e2c68aff2a0c9bf315e47d61951099fed65d8cb2c8d9dc388cb87e947e/numpy-2.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5e40e80299607f597e1a8a247ff8d71d79c5b52baa11cc1cce30aa92d2da6e0", size = 18576809, upload-time = "2025-07-24T20:52:51.015Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/4b4fd3efb0837ed252d0f583c5c35a75121038a8c4e065f2c259be06d2d8/numpy-2.3.2-cp314-cp314-win32.whl", hash = "sha256:7d6e390423cc1f76e1b8108c9b6889d20a7a1f59d9a60cac4a050fa734d6c1e2", size = 6366410, upload-time = "2025-07-24T20:56:44.949Z" }, + { url = "https://files.pythonhosted.org/packages/11/9e/b4c24a6b8467b61aced5c8dc7dcfce23621baa2e17f661edb2444a418040/numpy-2.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:b9d0878b21e3918d76d2209c924ebb272340da1fb51abc00f986c258cd5e957b", size = 12918821, upload-time = "2025-07-24T20:57:06.479Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0f/0dc44007c70b1007c1cef86b06986a3812dd7106d8f946c09cfa75782556/numpy-2.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:2738534837c6a1d0c39340a190177d7d66fdf432894f469728da901f8f6dc910", size = 10477303, upload-time = "2025-07-24T20:57:22.879Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3e/075752b79140b78ddfc9c0a1634d234cfdbc6f9bbbfa6b7504e445ad7d19/numpy-2.3.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4d002ecf7c9b53240be3bb69d80f86ddbd34078bae04d87be81c1f58466f264e", size = 21047524, upload-time = "2025-07-24T20:53:22.086Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6d/60e8247564a72426570d0e0ea1151b95ce5bd2f1597bb878a18d32aec855/numpy-2.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:293b2192c6bcce487dbc6326de5853787f870aeb6c43f8f9c6496db5b1781e45", size = 14300519, upload-time = "2025-07-24T20:53:44.053Z" }, + { url = "https://files.pythonhosted.org/packages/4d/73/d8326c442cd428d47a067070c3ac6cc3b651a6e53613a1668342a12d4479/numpy-2.3.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0a4f2021a6da53a0d580d6ef5db29947025ae8b35b3250141805ea9a32bbe86b", size = 5228972, upload-time = "2025-07-24T20:53:53.81Z" }, + { url = "https://files.pythonhosted.org/packages/34/2e/e71b2d6dad075271e7079db776196829019b90ce3ece5c69639e4f6fdc44/numpy-2.3.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9c144440db4bf3bb6372d2c3e49834cc0ff7bb4c24975ab33e01199e645416f2", size = 6737439, upload-time = "2025-07-24T20:54:04.742Z" }, + { url = "https://files.pythonhosted.org/packages/15/b0/d004bcd56c2c5e0500ffc65385eb6d569ffd3363cb5e593ae742749b2daa/numpy-2.3.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f92d6c2a8535dc4fe4419562294ff957f83a16ebdec66df0805e473ffaad8bd0", size = 14352479, upload-time = "2025-07-24T20:54:25.819Z" }, + { url = "https://files.pythonhosted.org/packages/11/e3/285142fcff8721e0c99b51686426165059874c150ea9ab898e12a492e291/numpy-2.3.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cefc2219baa48e468e3db7e706305fcd0c095534a192a08f31e98d83a7d45fb0", size = 16702805, upload-time = "2025-07-24T20:54:50.814Z" }, + { url = "https://files.pythonhosted.org/packages/33/c3/33b56b0e47e604af2c7cd065edca892d180f5899599b76830652875249a3/numpy-2.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76c3e9501ceb50b2ff3824c3589d5d1ab4ac857b0ee3f8f49629d0de55ecf7c2", size = 16133830, upload-time = "2025-07-24T20:55:17.306Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ae/7b1476a1f4d6a48bc669b8deb09939c56dd2a439db1ab03017844374fb67/numpy-2.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:122bf5ed9a0221b3419672493878ba4967121514b1d7d4656a7580cd11dddcbf", size = 18652665, upload-time = "2025-07-24T20:55:46.665Z" }, + { url = "https://files.pythonhosted.org/packages/14/ba/5b5c9978c4bb161034148ade2de9db44ec316fab89ce8c400db0e0c81f86/numpy-2.3.2-cp314-cp314t-win32.whl", hash = "sha256:6f1ae3dcb840edccc45af496f312528c15b1f79ac318169d094e85e4bb35fdf1", size = 6514777, upload-time = "2025-07-24T20:55:57.66Z" }, + { url = "https://files.pythonhosted.org/packages/eb/46/3dbaf0ae7c17cdc46b9f662c56da2054887b8d9e737c1476f335c83d33db/numpy-2.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b", size = 13111856, upload-time = "2025-07-24T20:56:17.318Z" }, + { url = "https://files.pythonhosted.org/packages/c1/9e/1652778bce745a67b5fe05adde60ed362d38eb17d919a540e813d30f6874/numpy-2.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:092aeb3449833ea9c0bf0089d70c29ae480685dd2377ec9cdbbb620257f84631", size = 10544226, upload-time = "2025-07-24T20:56:34.509Z" }, + { url = "https://files.pythonhosted.org/packages/cf/ea/50ebc91d28b275b23b7128ef25c3d08152bc4068f42742867e07a870a42a/numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15", size = 21130338, upload-time = "2025-07-24T20:57:54.37Z" }, + { url = "https://files.pythonhosted.org/packages/9f/57/cdd5eac00dd5f137277355c318a955c0d8fb8aa486020c22afd305f8b88f/numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec", size = 14375776, upload-time = "2025-07-24T20:58:16.303Z" }, + { url = "https://files.pythonhosted.org/packages/83/85/27280c7f34fcd305c2209c0cdca4d70775e4859a9eaa92f850087f8dea50/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712", size = 5304882, upload-time = "2025-07-24T20:58:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/48/b4/6500b24d278e15dd796f43824e69939d00981d37d9779e32499e823aa0aa/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c", size = 6818405, upload-time = "2025-07-24T20:58:37.341Z" }, + { url = "https://files.pythonhosted.org/packages/9b/c9/142c1e03f199d202da8e980c2496213509291b6024fd2735ad28ae7065c7/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296", size = 14419651, upload-time = "2025-07-24T20:58:59.048Z" }, + { url = "https://files.pythonhosted.org/packages/8b/95/8023e87cbea31a750a6c00ff9427d65ebc5fef104a136bfa69f76266d614/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981", size = 16760166, upload-time = "2025-07-24T21:28:56.38Z" }, + { url = "https://files.pythonhosted.org/packages/78/e3/6690b3f85a05506733c7e90b577e4762517404ea78bab2ca3a5cb1aeb78d/numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619", size = 12977811, upload-time = "2025-07-24T21:29:18.234Z" }, +] + +[[package]] +name = "orderly-set" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/88/39c83c35d5e97cc203e9e77a4f93bf87ec89cf6a22ac4818fdcc65d66584/orderly_set-5.5.0.tar.gz", hash = "sha256:e87185c8e4d8afa64e7f8160ee2c542a475b738bc891dc3f58102e654125e6ce", size = 27414, upload-time = "2025-07-10T20:10:55.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl", hash = "sha256:46f0b801948e98f427b412fcabb831677194c05c3b699b80de260374baa0b1e7", size = 13068, upload-time = "2025-07-10T20:10:54.377Z" }, +] + +[[package]] +name = "overture-schema" +source = { editable = "packages/overture-schema" } +dependencies = [ + { name = "overture-schema-addresses-theme" }, + { name = "overture-schema-base-theme" }, + { name = "overture-schema-buildings-theme" }, + { name = "overture-schema-core" }, + { name = "overture-schema-divisions-theme" }, + { name = "overture-schema-places-theme" }, + { name = "overture-schema-transportation-theme" }, + { name = "pydantic" }, + { name = "pyyaml" }, +] + +[package.dev-dependencies] +dev = [ + { name = "deepdiff" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pyyaml" }, + { name = "ruff" }, + { name = "yamlcore" }, +] + +[package.metadata] +requires-dist = [ + { name = "overture-schema-addresses-theme", editable = "packages/overture-schema-addresses-theme" }, + { name = "overture-schema-base-theme", editable = "packages/overture-schema-base-theme" }, + { name = "overture-schema-buildings-theme", editable = "packages/overture-schema-buildings-theme" }, + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "overture-schema-divisions-theme", editable = "packages/overture-schema-divisions-theme" }, + { name = "overture-schema-places-theme", editable = "packages/overture-schema-places-theme" }, + { name = "overture-schema-transportation-theme", editable = "packages/overture-schema-transportation-theme" }, + { name = "pydantic", specifier = ">=2.0" }, + { name = "pyyaml", specifier = ">=6.0.2" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "deepdiff" }, + { name = "mypy" }, + { name = "pytest", specifier = ">=7.0" }, + { name = "pytest-cov" }, + { name = "pyyaml" }, + { name = "ruff" }, + { name = "yamlcore", specifier = ">=0.0.4" }, +] + +[[package]] +name = "overture-schema-addresses-theme" +source = { editable = "packages/overture-schema-addresses-theme" } +dependencies = [ + { name = "overture-schema-core" }, + { name = "pydantic" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "pydantic", specifier = ">=2.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pytest", specifier = ">=7.0" }, + { name = "ruff" }, +] + +[[package]] +name = "overture-schema-base-theme" +source = { editable = "packages/overture-schema-base-theme" } +dependencies = [ + { name = "overture-schema-core" }, + { name = "pydantic" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "pydantic", specifier = ">=2.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pytest", specifier = ">=7.0" }, + { name = "ruff" }, +] + +[[package]] +name = "overture-schema-buildings-theme" +source = { editable = "packages/overture-schema-buildings-theme" } +dependencies = [ + { name = "overture-schema-core" }, + { name = "pydantic" }, +] + +[package.metadata] +requires-dist = [ + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "pydantic", specifier = ">=2.0" }, +] + +[[package]] +name = "overture-schema-core" +source = { editable = "packages/overture-schema-core" } +dependencies = [ + { name = "pydantic" }, + { name = "shapely" }, +] + +[package.dev-dependencies] +dev = [ + { name = "jsonpath-ng" }, + { name = "pytest-subtests" }, + { name = "types-pyyaml" }, + { name = "types-shapely" }, +] + +[package.metadata] +requires-dist = [ + { name = "pydantic", specifier = ">=2.0" }, + { name = "shapely", specifier = ">=2.1.1" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "jsonpath-ng", specifier = ">=1.7.0" }, + { name = "pytest-subtests", specifier = ">=0.14.2" }, + { name = "types-pyyaml", specifier = ">=6.0.12.20250516" }, + { name = "types-shapely", specifier = ">=2.1.0.20250710" }, +] + +[[package]] +name = "overture-schema-divisions-theme" +source = { editable = "packages/overture-schema-divisions-theme" } +dependencies = [ + { name = "overture-schema-core" }, + { name = "overture-schema-validation" }, + { name = "pydantic" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "overture-schema-validation", editable = "packages/overture-schema-validation" }, + { name = "pydantic", specifier = ">=2.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pytest", specifier = ">=7.0" }, + { name = "ruff" }, +] + +[[package]] +name = "overture-schema-foundation" +source = { editable = "packages/overture-schema-foundation" } +dependencies = [ + { name = "pydantic" }, + { name = "shapely" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "shapely", specifier = ">=2.0.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pytest", specifier = ">=7.0" }, + { name = "ruff" }, +] + +[[package]] +name = "overture-schema-places-theme" +source = { editable = "packages/overture-schema-places-theme" } +dependencies = [ + { name = "overture-schema-core" }, + { name = "overture-schema-validation" }, + { name = "pydantic", extra = ["email"] }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "overture-schema-validation", editable = "packages/overture-schema-validation" }, + { name = "pydantic", specifier = ">=2.0" }, + { name = "pydantic", extras = ["email"] }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pytest", specifier = ">=7.0" }, + { name = "ruff" }, +] + +[[package]] +name = "overture-schema-transportation-theme" +source = { editable = "packages/overture-schema-transportation-theme" } +dependencies = [ + { name = "overture-schema-core" }, + { name = "pydantic" }, +] + +[package.metadata] +requires-dist = [ + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "pydantic", specifier = ">=2.0" }, +] + +[[package]] +name = "overture-schema-validation" +source = { editable = "packages/overture-schema-validation" } +dependencies = [ + { name = "pydantic" }, + { name = "shapely" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "shapely", specifier = ">=2.0.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pytest", specifier = ">=7.0" }, + { name = "ruff" }, +] + +[[package]] +name = "overture-schema-workspace" +version = "0.0.0" +source = { virtual = "." } + +[package.dev-dependencies] +dev = [ + { name = "docformatter" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "docformatter", specifier = ">=1.7.7" }, + { name = "mypy", specifier = ">=1.17.0" }, + { name = "pytest", specifier = ">=8.4.1" }, + { name = "ruff", specifier = ">=0.12.4" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "ply" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/69/882ee5c9d017149285cab114ebeab373308ef0f874fcdac9beb90e0ac4da/ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3", size = 159130, upload-time = "2018-02-15T19:01:31.097Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567, upload-time = "2018-02-15T19:01:27.172Z" }, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, + { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, + { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, + { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, + { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, + { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, + { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, + { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, + { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, + { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, + { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, + { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, +] + +[[package]] +name = "pytest-cov" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, +] + +[[package]] +name = "pytest-subtests" +version = "0.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/30/6ec8dfc678ddfd1c294212bbd7088c52d3f7fbf3f05e6d8a440c13b9741a/pytest_subtests-0.14.2.tar.gz", hash = "sha256:7154a8665fd528ee70a76d00216a44d139dc3c9c83521a0f779f7b0ad4f800de", size = 18083, upload-time = "2025-06-13T10:50:01.636Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/d4/9bf12e59fb882b0cf4f993871e1adbee094802224c429b00861acee1a169/pytest_subtests-0.14.2-py3-none-any.whl", hash = "sha256:8da0787c994ab372a13a0ad7d390533ad2e4385cac167b3ac501258c885d0b66", size = 9115, upload-time = "2025-06-13T10:50:00.543Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + +[[package]] +name = "ruff" +version = "0.12.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" }, + { url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" }, + { url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" }, + { url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" }, + { url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" }, + { url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" }, + { url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" }, + { url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" }, + { url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" }, + { url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" }, + { url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" }, +] + +[[package]] +name = "shapely" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/fa/f18025c95b86116dd8f1ec58cab078bd59ab51456b448136ca27463be533/shapely-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8ccc872a632acb7bdcb69e5e78df27213f7efd195882668ffba5405497337c6", size = 1825117, upload-time = "2025-05-19T11:03:43.547Z" }, + { url = "https://files.pythonhosted.org/packages/c7/65/46b519555ee9fb851234288be7c78be11e6260995281071d13abf2c313d0/shapely-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f24f2ecda1e6c091da64bcbef8dd121380948074875bd1b247b3d17e99407099", size = 1628541, upload-time = "2025-05-19T11:03:45.162Z" }, + { url = "https://files.pythonhosted.org/packages/29/51/0b158a261df94e33505eadfe737db9531f346dfa60850945ad25fd4162f1/shapely-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45112a5be0b745b49e50f8829ce490eb67fefb0cea8d4f8ac5764bfedaa83d2d", size = 2948453, upload-time = "2025-05-19T11:03:46.681Z" }, + { url = "https://files.pythonhosted.org/packages/a9/4f/6c9bb4bd7b1a14d7051641b9b479ad2a643d5cbc382bcf5bd52fd0896974/shapely-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c10ce6f11904d65e9bbb3e41e774903c944e20b3f0b282559885302f52f224a", size = 3057029, upload-time = "2025-05-19T11:03:48.346Z" }, + { url = "https://files.pythonhosted.org/packages/89/0b/ad1b0af491d753a83ea93138eee12a4597f763ae12727968d05934fe7c78/shapely-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:61168010dfe4e45f956ffbbaf080c88afce199ea81eb1f0ac43230065df320bd", size = 3894342, upload-time = "2025-05-19T11:03:49.602Z" }, + { url = "https://files.pythonhosted.org/packages/7d/96/73232c5de0b9fdf0ec7ddfc95c43aaf928740e87d9f168bff0e928d78c6d/shapely-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cacf067cdff741cd5c56a21c52f54ece4e4dad9d311130493a791997da4a886b", size = 4056766, upload-time = "2025-05-19T11:03:51.252Z" }, + { url = "https://files.pythonhosted.org/packages/43/cc/eec3c01f754f5b3e0c47574b198f9deb70465579ad0dad0e1cef2ce9e103/shapely-2.1.1-cp310-cp310-win32.whl", hash = "sha256:23b8772c3b815e7790fb2eab75a0b3951f435bc0fce7bb146cb064f17d35ab4f", size = 1523744, upload-time = "2025-05-19T11:03:52.624Z" }, + { url = "https://files.pythonhosted.org/packages/50/fc/a7187e6dadb10b91e66a9e715d28105cde6489e1017cce476876185a43da/shapely-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:2c7b2b6143abf4fa77851cef8ef690e03feade9a0d48acd6dc41d9e0e78d7ca6", size = 1703061, upload-time = "2025-05-19T11:03:54.695Z" }, + { url = "https://files.pythonhosted.org/packages/19/97/2df985b1e03f90c503796ad5ecd3d9ed305123b64d4ccb54616b30295b29/shapely-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587a1aa72bc858fab9b8c20427b5f6027b7cbc92743b8e2c73b9de55aa71c7a7", size = 1819368, upload-time = "2025-05-19T11:03:55.937Z" }, + { url = "https://files.pythonhosted.org/packages/56/17/504518860370f0a28908b18864f43d72f03581e2b6680540ca668f07aa42/shapely-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fa5c53b0791a4b998f9ad84aad456c988600757a96b0a05e14bba10cebaaaea", size = 1625362, upload-time = "2025-05-19T11:03:57.06Z" }, + { url = "https://files.pythonhosted.org/packages/36/a1/9677337d729b79fce1ef3296aac6b8ef4743419086f669e8a8070eff8f40/shapely-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aabecd038841ab5310d23495253f01c2a82a3aedae5ab9ca489be214aa458aa7", size = 2999005, upload-time = "2025-05-19T11:03:58.692Z" }, + { url = "https://files.pythonhosted.org/packages/a2/17/e09357274699c6e012bbb5a8ea14765a4d5860bb658df1931c9f90d53bd3/shapely-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586f6aee1edec04e16227517a866df3e9a2e43c1f635efc32978bb3dc9c63753", size = 3108489, upload-time = "2025-05-19T11:04:00.059Z" }, + { url = "https://files.pythonhosted.org/packages/17/5d/93a6c37c4b4e9955ad40834f42b17260ca74ecf36df2e81bb14d12221b90/shapely-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9878b9e37ad26c72aada8de0c9cfe418d9e2ff36992a1693b7f65a075b28647", size = 3945727, upload-time = "2025-05-19T11:04:01.786Z" }, + { url = "https://files.pythonhosted.org/packages/a3/1a/ad696648f16fd82dd6bfcca0b3b8fbafa7aacc13431c7fc4c9b49e481681/shapely-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9a531c48f289ba355e37b134e98e28c557ff13965d4653a5228d0f42a09aed0", size = 4109311, upload-time = "2025-05-19T11:04:03.134Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/150dd245beab179ec0d4472bf6799bf18f21b1efbef59ac87de3377dbf1c/shapely-2.1.1-cp311-cp311-win32.whl", hash = "sha256:4866de2673a971820c75c0167b1f1cd8fb76f2d641101c23d3ca021ad0449bab", size = 1522982, upload-time = "2025-05-19T11:04:05.217Z" }, + { url = "https://files.pythonhosted.org/packages/93/5b/842022c00fbb051083c1c85430f3bb55565b7fd2d775f4f398c0ba8052ce/shapely-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:20a9d79958b3d6c70d8a886b250047ea32ff40489d7abb47d01498c704557a93", size = 1703872, upload-time = "2025-05-19T11:04:06.791Z" }, + { url = "https://files.pythonhosted.org/packages/fb/64/9544dc07dfe80a2d489060791300827c941c451e2910f7364b19607ea352/shapely-2.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2827365b58bf98efb60affc94a8e01c56dd1995a80aabe4b701465d86dcbba43", size = 1833021, upload-time = "2025-05-19T11:04:08.022Z" }, + { url = "https://files.pythonhosted.org/packages/07/aa/fb5f545e72e89b6a0f04a0effda144f5be956c9c312c7d4e00dfddbddbcf/shapely-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c551f7fa7f1e917af2347fe983f21f212863f1d04f08eece01e9c275903fad", size = 1643018, upload-time = "2025-05-19T11:04:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/03/46/61e03edba81de729f09d880ce7ae5c1af873a0814206bbfb4402ab5c3388/shapely-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78dec4d4fbe7b1db8dc36de3031767e7ece5911fb7782bc9e95c5cdec58fb1e9", size = 2986417, upload-time = "2025-05-19T11:04:10.56Z" }, + { url = "https://files.pythonhosted.org/packages/1f/1e/83ec268ab8254a446b4178b45616ab5822d7b9d2b7eb6e27cf0b82f45601/shapely-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:872d3c0a7b8b37da0e23d80496ec5973c4692920b90de9f502b5beb994bbaaef", size = 3098224, upload-time = "2025-05-19T11:04:11.903Z" }, + { url = "https://files.pythonhosted.org/packages/f1/44/0c21e7717c243e067c9ef8fa9126de24239f8345a5bba9280f7bb9935959/shapely-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2e2b9125ebfbc28ecf5353511de62f75a8515ae9470521c9a693e4bb9fbe0cf1", size = 3925982, upload-time = "2025-05-19T11:04:13.224Z" }, + { url = "https://files.pythonhosted.org/packages/15/50/d3b4e15fefc103a0eb13d83bad5f65cd6e07a5d8b2ae920e767932a247d1/shapely-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4b96cea171b3d7f6786976a0520f178c42792897653ecca0c5422fb1e6946e6d", size = 4089122, upload-time = "2025-05-19T11:04:14.477Z" }, + { url = "https://files.pythonhosted.org/packages/bd/05/9a68f27fc6110baeedeeebc14fd86e73fa38738c5b741302408fb6355577/shapely-2.1.1-cp312-cp312-win32.whl", hash = "sha256:39dca52201e02996df02e447f729da97cfb6ff41a03cb50f5547f19d02905af8", size = 1522437, upload-time = "2025-05-19T11:04:16.203Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e9/a4560e12b9338842a1f82c9016d2543eaa084fce30a1ca11991143086b57/shapely-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:13d643256f81d55a50013eff6321142781cf777eb6a9e207c2c9e6315ba6044a", size = 1703479, upload-time = "2025-05-19T11:04:18.497Z" }, + { url = "https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3004a644d9e89e26c20286d5fdc10f41b1744c48ce910bd1867fdff963fe6c48", size = 1832107, upload-time = "2025-05-19T11:04:19.736Z" }, + { url = "https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1415146fa12d80a47d13cfad5310b3c8b9c2aa8c14a0c845c9d3d75e77cb54f6", size = 1642355, upload-time = "2025-05-19T11:04:21.035Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/6d28b43d53fea56de69c744e34c2b999ed4042f7a811dc1bceb876071c95/shapely-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21fcab88b7520820ec16d09d6bea68652ca13993c84dffc6129dc3607c95594c", size = 2968871, upload-time = "2025-05-19T11:04:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ce6a5cc52c974b291237a96c08c5592e50f066871704fb5b12be2639d9026a", size = 3080830, upload-time = "2025-05-19T11:04:23.997Z" }, + { url = "https://files.pythonhosted.org/packages/1d/fe/f4a03d81abd96a6ce31c49cd8aaba970eaaa98e191bd1e4d43041e57ae5a/shapely-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:04e4c12a45a1d70aeb266618d8cf81a2de9c4df511b63e105b90bfdfb52146de", size = 3908961, upload-time = "2025-05-19T11:04:25.702Z" }, + { url = "https://files.pythonhosted.org/packages/ef/59/7605289a95a6844056a2017ab36d9b0cb9d6a3c3b5317c1f968c193031c9/shapely-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ca74d851ca5264aae16c2b47e96735579686cb69fa93c4078070a0ec845b8d8", size = 4079623, upload-time = "2025-05-19T11:04:27.171Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4d/9fea036eff2ef4059d30247128b2d67aaa5f0b25e9fc27e1d15cc1b84704/shapely-2.1.1-cp313-cp313-win32.whl", hash = "sha256:fd9130501bf42ffb7e0695b9ea17a27ae8ce68d50b56b6941c7f9b3d3453bc52", size = 1521916, upload-time = "2025-05-19T11:04:28.405Z" }, + { url = "https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:ab8d878687b438a2f4c138ed1a80941c6ab0029e0f4c785ecfe114413b498a97", size = 1702746, upload-time = "2025-05-19T11:04:29.643Z" }, + { url = "https://files.pythonhosted.org/packages/60/36/b1452e3e7f35f5f6454d96f3be6e2bb87082720ff6c9437ecc215fa79be0/shapely-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c062384316a47f776305ed2fa22182717508ffdeb4a56d0ff4087a77b2a0f6d", size = 1833482, upload-time = "2025-05-19T11:04:30.852Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ca/8e6f59be0718893eb3e478141285796a923636dc8f086f83e5b0ec0036d0/shapely-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4ecf6c196b896e8f1360cc219ed4eee1c1e5f5883e505d449f263bd053fb8c05", size = 1642256, upload-time = "2025-05-19T11:04:32.068Z" }, + { url = "https://files.pythonhosted.org/packages/ab/78/0053aea449bb1d4503999525fec6232f049abcdc8df60d290416110de943/shapely-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb00070b4c4860f6743c600285109c273cca5241e970ad56bb87bef0be1ea3a0", size = 3016614, upload-time = "2025-05-19T11:04:33.7Z" }, + { url = "https://files.pythonhosted.org/packages/ee/53/36f1b1de1dfafd1b457dcbafa785b298ce1b8a3e7026b79619e708a245d5/shapely-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14a9afa5fa980fbe7bf63706fdfb8ff588f638f145a1d9dbc18374b5b7de913", size = 3093542, upload-time = "2025-05-19T11:04:34.952Z" }, + { url = "https://files.pythonhosted.org/packages/b9/bf/0619f37ceec6b924d84427c88835b61f27f43560239936ff88915c37da19/shapely-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b640e390dabde790e3fb947198b466e63223e0a9ccd787da5f07bcb14756c28d", size = 3945961, upload-time = "2025-05-19T11:04:36.32Z" }, + { url = "https://files.pythonhosted.org/packages/93/c9/20ca4afeb572763b07a7997f00854cb9499df6af85929e93012b189d8917/shapely-2.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:69e08bf9697c1b73ec6aa70437db922bafcea7baca131c90c26d59491a9760f9", size = 4089514, upload-time = "2025-05-19T11:04:37.683Z" }, + { url = "https://files.pythonhosted.org/packages/33/6a/27036a5a560b80012a544366bceafd491e8abb94a8db14047b5346b5a749/shapely-2.1.1-cp313-cp313t-win32.whl", hash = "sha256:ef2d09d5a964cc90c2c18b03566cf918a61c248596998a0301d5b632beadb9db", size = 1540607, upload-time = "2025-05-19T11:04:38.925Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/5e9b3ba5c7aa7ebfaf269657e728067d16a7c99401c7973ddf5f0cf121bd/shapely-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8cb8f17c377260452e9d7720eeaf59082c5f8ea48cf104524d953e5d36d4bdb7", size = 1723061, upload-time = "2025-05-19T11:04:40.082Z" }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20250516" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378, upload-time = "2025-05-16T03:08:04.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312, upload-time = "2025-05-16T03:08:04.019Z" }, +] + +[[package]] +name = "types-shapely" +version = "2.1.0.20250710" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/70/37f63bc62aefde18795bd807381f7e2c2b7884f7f1ddaed2e6f727a099f9/types_shapely-2.1.0.20250710.tar.gz", hash = "sha256:f93067996f5203dffe57f57a130ae757b7e83609b046ea9631de8b900e4be77b", size = 26205, upload-time = "2025-07-10T03:15:50.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/f1/bc36b6a7dbe742b0dc75c0fe6b4f27a2a37dedcf63bac9afe55bdb998870/types_shapely-2.1.0.20250710-py3-none-any.whl", hash = "sha256:cf58a2f6a79ec7cbdaf2ba8a8d744842bd1f5ae159afc2512db552b2c67219f4", size = 37696, upload-time = "2025-07-10T03:15:49.852Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, +] + +[[package]] +name = "untokenize" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2", size = 3099, upload-time = "2014-02-08T16:30:40.631Z" } + +[[package]] +name = "yamlcore" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/d4/113dfc4c85f945de13b045a20eca50be5f6efa33342d9a9bd6162d26b288/yamlcore-0.0.4.tar.gz", hash = "sha256:c2b1a08dd117cf76a70ee124e936b3c568b0b5c4e5131e69b513f4a877a44881", size = 10456, upload-time = "2024-10-09T17:26:02.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/47/632498fd9a3fe4f175e5de71162dd8cd9b2141180a98f0bef6af293aeaa6/yamlcore-0.0.4-py3-none-any.whl", hash = "sha256:f82a7f7d16baf6531a8c3266b0b63ece251ee2abaf2b9eee040a2f64c8fc618d", size = 5969, upload-time = "2024-10-09T17:26:01.507Z" }, +] From ca9a048eb6351d215b1f1e64200b6d98b7b78037 Mon Sep 17 00:00:00 2001 From: schapper Date: Thu, 2 Oct 2025 14:39:44 -0700 Subject: [PATCH 12/20] wip - Add pdoc and slightly modify foundation to make it pdoc friendly --- .../schema/foundation/primitive/__init__.py | 108 +++++++++++++---- .../schema/foundation/primitive/num.py | 92 -------------- pyproject.toml | 1 + uv.lock | 113 ++++++++++++++++++ 4 files changed, 199 insertions(+), 115 deletions(-) diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py index b3b8fa6a7..c57d8776a 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py @@ -1,6 +1,4 @@ """ -primitive -========= Primitive data types. This module provides a set of primitive types that can be used as fields in Pydantic models but are @@ -12,17 +10,11 @@ targets, for example, the Parquet format or Spark dataframes. They come with built-in Pydantic constraints, but also specific documented behavior expectations so they can be supported by other serialization targets. +""" -Modules -------- -bbox : module - Bounding box type. -geom : module - Geometry type. -num : module - Numeric primitives such as int32 and float64. +from typing import Annotated, NewType, TypeAlias -""" +from pydantic import Field from .bbox import BBox from .geom import ( @@ -30,24 +22,94 @@ GeometryType, GeometryTypeConstraint, ) -from .num import ( - float32, - float64, - int8, - int16, - int32, - int64, - pct, - uint8, - uint16, - uint32, -) + +uint8: TypeAlias = NewType("uint8", Annotated[int, Field(ge=0, le=255)]) # type: ignore [type-arg] +""" +Portable 8-bit unsigned integer. + +This is an `int` at runtime, but using `uint8` for Pydantic model fields instead of `int` makes them +portable across different serialization and validation platforms. +""" + +uint16: TypeAlias = NewType("uint16", Annotated[int, Field(ge=0, le=65535)]) # type: ignore[type-arg] +""" +Portable 16-bit unsigned integer. + +This is an `int` at runtime, but using `uint16` for Pydantic model fields instead of `int` makes +them portable across different serialization and validation platforms. +""" + +uint32: TypeAlias = NewType("uint32", Annotated[int, Field(ge=0, le=4294967295)]) # type: ignore[type-arg] +""" +Portable 32-bit unsigned integer. + +This is an `int` at runtime, but using `uint32` for Pydantic model fields instead of `int` makes +them portable across different serialization and validation platforms. +""" + +int8: TypeAlias = NewType("int8", Annotated[int, Field(ge=-128, le=127)]) # type: ignore[type-arg] +""" +Portable 8-bit signed integer. + +This is an `int` at runtime, but using `int8` for Pydantic model fields instead of `int` makes them +portable across different serialization and validation platforms. +""" + +int16: TypeAlias = NewType("int16", Annotated[int, Field(ge=-32768, le=32767)]) # type: ignore[type-arg] +""" +Portable 16-bit signed integer. + +This is an `int` at runtime, but using `int16` for Pydantic model fields instead of `int` makes them +portable across different serialization and validation platforms. +""" + +int32: TypeAlias = NewType("int32", Annotated[int, Field(ge=-(2**31), le=2**31 - 1)]) # type: ignore[type-arg] +""" +Portable 32-bit signed integer. + +This is an `int` at runtime, but using `int32` for Pydantic model fields instead of `int` makes them +portable across different serialization and validation platforms. +""" + +int64: TypeAlias = NewType("int64", Annotated[int, Field(ge=-(2**63), le=2**63 - 1)]) # type: ignore[type-arg] +""" +Portable 64-bit signed integer. + +This is an `int` at runtime, but using `int64` for Pydantic model fields instead of `int` makes them +portable across different serialization and validation platforms. +""" + +float32: TypeAlias = NewType("float32", float) # type: ignore[type-arg] +""" +Portable IEEE 32-bit floating point number. + +This is a `float` at runtime, but using `float32` for Pydantic model fields instead of `float` makes +them portable across different serialization and validation platforms. +""" + +float64: TypeAlias = NewType("float64", float) # type: ignore[type-arg] +""" +Portable IEEE 64-bit floating point number. + +This is a `float` at runtime, but using `float64` for Pydantic model fields instead of `float` makes +them portable across different serialization and validation platforms. +""" + +pct: TypeAlias = NewType("pct", Annotated[float, Field(ge=0, le=1)]) # type: ignore[type-arg] +""" +Portable percent value in the range [0, 1] where 0 represents 0% and 1 represents 100%. + +This is a `float` at runtime, but using `pct` for Pydantic model fields instead of `float` makes +them portable across different serialization and validation platforms. +""" + __all__ = [ "BBox", "Geometry", "GeometryType", "GeometryTypeConstraint", + "int8", "int16", "int32", diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py index 42ae1ffef..8b1378917 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py @@ -1,93 +1 @@ -from typing import Annotated, NewType, TypeAlias -from pydantic import Field - -uint8: TypeAlias = NewType("uint8", Annotated[int, Field(ge=0, le=255)]) # type: ignore [type-arg] -uint8.__doc__ = """ -uint8 : NewType - Portable 8-bit unsigned integer. - - This is an int at runtime, but using uint8 for fields instead of int makes them portable across - different serialization and validation platforms. -""" - -uint16: TypeAlias = NewType("uint16", Annotated[int, Field(ge=0, le=65535)]) # type: ignore[type-arg] -uint16.__doc__ = """ -uint16 : NewType - Portable 16-bit unsigned integer. - - This is an int at runtime, but using uint16 for fields instead of int makes them portable across - different serialization and validation platforms. -""" - -uint32: TypeAlias = NewType("uint32", Annotated[int, Field(ge=0, le=4294967295)]) # type: ignore[type-arg] -uint32.__doc__ = """ -uint32 : NewType - Portable 32-bit unsigned integer. - - This is an int at runtime, but using uint32 for fields instead of int makes them portable across - different serialization and validation platforms. -""" - -int8: TypeAlias = NewType("int8", Annotated[int, Field(ge=-128, le=127)]) # type: ignore[type-arg] -int8.__doc__ = """ -int8 : NewType - Portable 8-bit signed integer. - - This is an int at runtime, but using int8 for fields instead of int makes them portable across - different serialization and validation platforms. -""" - -int16: TypeAlias = NewType("int16", Annotated[int, Field(ge=-32768, le=32767)]) # type: ignore[type-arg] -int16.__doc__ = """ -int16 : NewType - Portable 16-bit signed integer. - - This is an int at runtime, but using int16 for fields instead of int makes them portable across - different serialization and validation platforms. -""" - -int32: TypeAlias = NewType("int32", Annotated[int, Field(ge=-(2**31), le=2**31 - 1)]) # type: ignore[type-arg] -int32.__doc__ = """ -int32 : NewType - Portable 32-bit signed integer. - - This is an int at runtime, but using int32 for fields instead of int makes them portable across - different serialization and validation platforms. -""" - -int64: TypeAlias = NewType("int64", Annotated[int, Field(ge=-(2**63), le=2**63 - 1)]) # type: ignore[type-arg] -int64.__doc__ = """ -int64 : NewType - Portable 64-bit signed integer. - - This is an int at runtime, but using int64 for fields instead of int makes them portable across - different serialization and validation platforms. -""" - -float32: TypeAlias = NewType("float32", float) # type: ignore[type-arg] -float32.__doc__ = """ -float32 : NewType - Portable IEEE 32-bit floating point number. - - This is a float at runtime, but using float32 for fields instead of float makes them portable - across different serialization and validation platforms. -""" - -float64: TypeAlias = NewType("float64", float) # type: ignore[type-arg] -float64.__doc__ = """ -float64 : NewType - Portable IEEE 64-bit floating point number. - - This is a float at runtime, but using float64 for fields instead of float makes them portable - across different serialization and validation platforms. -""" - -pct: TypeAlias = NewType("pct", Annotated[float, Field(ge=0, le=1)]) # type: ignore[type-arg] -pct.__doc__ = """ -pct : NewType - Portable percent value in the range [0, 1] where 0 represents 0% and 1 represents 100%. - - This is a float at runtime, but using pct for fields instead of float makes them portable - across different serialization and validation platforms. -""" diff --git a/pyproject.toml b/pyproject.toml index 9d86cce3d..35183faad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ warn_unused_configs = true dev = [ "docformatter>=1.7.7", "mypy>=1.17.0", + "pdoc>=15.0.4", "pytest>=8.4.1", "pytest-cov", "ruff>=0.12.4", diff --git a/uv.lock b/uv.lock index e294d320f..4e8a1b60b 100644 --- a/uv.lock +++ b/uv.lock @@ -255,6 +255,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + [[package]] name = "jsonpath-ng" version = "1.7.0" @@ -267,6 +279,91 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/35/5a/73ecb3d82f8615f32ccdadeb9356726d6cae3a4bbc840b437ceb95708063/jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6", size = 30105, upload-time = "2024-11-20T17:58:30.418Z" }, ] +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + [[package]] name = "mypy" version = "1.17.0" @@ -763,6 +860,7 @@ source = { virtual = "." } dev = [ { name = "docformatter" }, { name = "mypy" }, + { name = "pdoc" }, { name = "pytest" }, { name = "ruff" }, ] @@ -773,6 +871,7 @@ dev = [ dev = [ { name = "docformatter", specifier = ">=1.7.7" }, { name = "mypy", specifier = ">=1.17.0" }, + { name = "pdoc", specifier = ">=15.0.4" }, { name = "pytest", specifier = ">=8.4.1" }, { name = "ruff", specifier = ">=0.12.4" }, ] @@ -795,6 +894,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] +[[package]] +name = "pdoc" +version = "15.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/5c/e94c1ab4aa2f8a9cc29d81e1c513c6216946cb3a90957ef7115b12e9363d/pdoc-15.0.4.tar.gz", hash = "sha256:cf9680f10f5b4863381f44ef084b1903f8f356acb0d4cc6b64576ba9fb712c82", size = 155678, upload-time = "2025-06-04T17:05:49.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/2c/87250ac73ca8730b2c4e0185b573585f0b42e09562132e6c29d00b3a9bb9/pdoc-15.0.4-py3-none-any.whl", hash = "sha256:f9028e85e7bb8475b054e69bde1f6d26fc4693d25d9fa1b1ce9009bec7f7a5c4", size = 145978, upload-time = "2025-06-04T17:05:48.473Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" From b8c14acbc3c7db736ac1a8b74a270d0f2dcf4e3f Mon Sep 17 00:00:00 2001 From: schapper Date: Thu, 2 Oct 2025 16:13:05 -0700 Subject: [PATCH 13/20] wip - fix doctests and run them as part of `make check` --- Makefile | 10 +++++++++- .../src/overture/schema/core/ref.py | 4 ++-- .../overture/schema/foundation/primitive/__init__.py | 1 - .../src/overture/schema/foundation/primitive/bbox.py | 4 ++-- .../src/overture/schema/foundation/primitive/geom.py | 11 +++++++---- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 0b1383de3..9d0d350ec 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ default: test-all uv-sync: @uv sync --all-packages -check: test uv-sync +check: test doctest @uv run ruff check -q packages/ @$(MAKE) mypy @uv run ruff format --check packages/ @@ -16,6 +16,14 @@ test-all: uv-sync test: uv-sync @uv run pytest packages/ -x +doctest: uv-sync + @# $$ escapes $ for make - sed needs literal $ for end-of-line anchor + @find packages/*/src -name "*.py" -type f -not -name "__*" \ + | sed 's|^packages/[^/]*/src/||' \ + | sed 's|/|.|g' \ + | sed 's|\.py$$||' \ + | xargs uv run python -c 'import doctest, importlib, sys; [doctest.testmod(importlib.import_module(m)) for m in sys.argv[1:]]' + # mypy type checking with namespace package support mypy: uv-sync @cd packages && uv run mypy --no-error-summary --namespace-packages \ diff --git a/packages/overture-schema-core/src/overture/schema/core/ref.py b/packages/overture-schema-core/src/overture/schema/core/ref.py index 60564887a..0522f4b60 100644 --- a/packages/overture-schema-core/src/overture/schema/core/ref.py +++ b/packages/overture-schema-core/src/overture/schema/core/ref.py @@ -53,9 +53,9 @@ class that is said to "hold the reference") and the relatee. >>> from overture.schema.core.ref import Reference, Relationship >>> from overture.schema.core.types import Id >>> class Park(Feature): - >>> pass + ... pass >>> class ParkBench(Feature): - >>> park_id: Annotated[Id, Reference(Relationship.BELONGS_TO, Park)] + ... park_id: Annotated[Id, Reference(Relationship.BELONGS_TO, Park)] """ relationship: Relationship diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py index c57d8776a..946e11577 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py @@ -109,7 +109,6 @@ "Geometry", "GeometryType", "GeometryTypeConstraint", - "int8", "int16", "int32", diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/bbox.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/bbox.py index fdb4b2073..55239487e 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/bbox.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/bbox.py @@ -42,12 +42,12 @@ class BBox: Read a bounding box from its GeoJSON representation: >>> BBox.from_geo_json([1, 2, 3, 4]) - BBox(xmin=1, ymin=2, xmax=3, ymax=4) + BBox(1, 2, 3, 4) Read a bounding box from its GeoJSON representation (simplified form for easier interactive use): >>> BBox.from_geo_json(1, 2, 3, 4) - BBox(xmin=1, ymin=2, xmax=3, ymax=4) + BBox(1, 2, 3, 4) """ __slots__ = ("_xmin", "_ymin", "_xmax", "_ymax") diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geom.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geom.py index 76b5a5cff..f22c2845a 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geom.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geom.py @@ -91,10 +91,13 @@ class GeometryTypeConstraint: -------- Limit allowed geometry of a feature type to line string and multi line string. - >>> from overture.schema.core.geometry import Geometry - >>> from overture.schema.core.models import Feature - >>> class MyFeature(Feature): - >>> geometry: Annotated[Geometry, GeometryTypeConstraint("line_string", "multi_line_string")] + >>> from typing import Annotated + >>> from pydantic import BaseModel; + >>> class MyModel(BaseModel): + ... geometry: Annotated[ + ... Geometry, + ... GeometryTypeConstraint(GeometryType.LINE_STRING, GeometryType.MULTI_LINE_STRING) + ... ] """ allowed_types: tuple[GeometryType, ...] From 3ef76af7c309a06c6c013a0e7a47a7703b5218a8 Mon Sep 17 00:00:00 2001 From: schapper Date: Thu, 2 Oct 2025 16:22:58 -0700 Subject: [PATCH 14/20] add make target for docs format checking but don't make it mandatory yet... ...Too many broken docs to bother --- Makefile | 4 ++++ pyproject.toml | 1 + uv.lock | 23 +++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/Makefile b/Makefile index 9d0d350ec..67c2edc31 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,10 @@ test-all: uv-sync test: uv-sync @uv run pytest packages/ -x +docformat: + @find packages/*/src -name "*.py" -type f -not -name "__*" \ + | xargs uv run pydocstyle --convention=numpy --add-ignore=D105 + doctest: uv-sync @# $$ escapes $ for make - sed needs literal $ for end-of-line anchor @find packages/*/src -name "*.py" -type f -not -name "__*" \ diff --git a/pyproject.toml b/pyproject.toml index 35183faad..1b26b2a0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ dev = [ "docformatter>=1.7.7", "mypy>=1.17.0", "pdoc>=15.0.4", + "pydocstyle>=6.3.0", "pytest>=8.4.1", "pytest-cov", "ruff>=0.12.4", diff --git a/uv.lock b/uv.lock index 4e8a1b60b..24e17594d 100644 --- a/uv.lock +++ b/uv.lock @@ -861,6 +861,7 @@ dev = [ { name = "docformatter" }, { name = "mypy" }, { name = "pdoc" }, + { name = "pydocstyle" }, { name = "pytest" }, { name = "ruff" }, ] @@ -872,6 +873,7 @@ dev = [ { name = "docformatter", specifier = ">=1.7.7" }, { name = "mypy", specifier = ">=1.17.0" }, { name = "pdoc", specifier = ">=15.0.4" }, + { name = "pydocstyle", specifier = ">=6.3.0" }, { name = "pytest", specifier = ">=8.4.1" }, { name = "ruff", specifier = ">=0.12.4" }, ] @@ -1033,6 +1035,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, ] +[[package]] +name = "pydocstyle" +version = "6.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "snowballstemmer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/d5385ca59fd065e3c6a5fe19f9bc9d5ea7f2509fa8c9c22fb6b2031dd953/pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1", size = 36796, upload-time = "2023-01-17T20:29:19.838Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/ea/99ddefac41971acad68f14114f38261c1f27dac0b3ec529824ebc739bdaa/pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019", size = 38038, upload-time = "2023-01-17T20:29:18.094Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -1208,6 +1222,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/f1/5e9b3ba5c7aa7ebfaf269657e728067d16a7c99401c7973ddf5f0cf121bd/shapely-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8cb8f17c377260452e9d7720eeaf59082c5f8ea48cf104524d953e5d36d4bdb7", size = 1723061, upload-time = "2025-05-19T11:04:40.082Z" }, ] +[[package]] +name = "snowballstemmer" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, +] + [[package]] name = "tomli" version = "2.2.1" From 53428d00940e7847a77aecd782ae7ba70fd6e856 Mon Sep 17 00:00:00 2001 From: schapper Date: Thu, 2 Oct 2025 16:36:57 -0700 Subject: [PATCH 15/20] wip - CHECKS FAILING NOW - make mypy target work on parameterized packages --- Makefile | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 67c2edc31..1454a0c06 100644 --- a/Makefile +++ b/Makefile @@ -30,16 +30,13 @@ doctest: uv-sync # mypy type checking with namespace package support mypy: uv-sync - @cd packages && uv run mypy --no-error-summary --namespace-packages \ - -p overture.schema \ - -p overture.schema.addresses \ - -p overture.schema.base \ - -p overture.schema.buildings \ - -p overture.schema.core \ - -p overture.schema.divisions \ - -p overture.schema.places \ - -p overture.schema.transportation \ - -p overture.schema.validation + @# $$ escapes $ for make - sed needs literal $ for end-of-line anchor + @find packages -maxdepth 1 -type d -name "overture-schema*" \ + | sort \ + | sed 's:-theme$$::' \ + | tr - . \ + | sed 's:^packages/\|:-p :' \ + | xargs uv run mypy --no-error-summary @uv run mypy --no-error-summary packages/*/tests/*.py reset-baseline-schemas: From 5a74b04129efac3aa39164a4ae061e4b7db469e1 Mon Sep 17 00:00:00 2001 From: schapper Date: Thu, 2 Oct 2025 16:39:34 -0700 Subject: [PATCH 16/20] wip - Fix broken mypy checks in foundation package --- .../schema/foundation/primitive/__init__.py | 22 +++++++++---------- .../schema/foundation/primitive/num.py | 1 - .../src/overture/schema/foundation/string.py | 20 ++++++++--------- 3 files changed, 21 insertions(+), 22 deletions(-) delete mode 100644 packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py index 946e11577..3a075ab85 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py @@ -12,7 +12,7 @@ serialization targets. """ -from typing import Annotated, NewType, TypeAlias +from typing import Annotated, NewType from pydantic import Field @@ -23,7 +23,7 @@ GeometryTypeConstraint, ) -uint8: TypeAlias = NewType("uint8", Annotated[int, Field(ge=0, le=255)]) # type: ignore [type-arg] +uint8 = NewType("uint8", Annotated[int, Field(ge=0, le=255)]) # type: ignore [type-arg] """ Portable 8-bit unsigned integer. @@ -31,7 +31,7 @@ portable across different serialization and validation platforms. """ -uint16: TypeAlias = NewType("uint16", Annotated[int, Field(ge=0, le=65535)]) # type: ignore[type-arg] +uint16 = NewType("uint16", Annotated[int, Field(ge=0, le=65535)]) # type: ignore[type-arg] """ Portable 16-bit unsigned integer. @@ -39,7 +39,7 @@ them portable across different serialization and validation platforms. """ -uint32: TypeAlias = NewType("uint32", Annotated[int, Field(ge=0, le=4294967295)]) # type: ignore[type-arg] +uint32 = NewType("uint32", Annotated[int, Field(ge=0, le=4294967295)]) # type: ignore[type-arg] """ Portable 32-bit unsigned integer. @@ -47,7 +47,7 @@ them portable across different serialization and validation platforms. """ -int8: TypeAlias = NewType("int8", Annotated[int, Field(ge=-128, le=127)]) # type: ignore[type-arg] +int8 = NewType("int8", Annotated[int, Field(ge=-128, le=127)]) # type: ignore[type-arg] """ Portable 8-bit signed integer. @@ -55,7 +55,7 @@ portable across different serialization and validation platforms. """ -int16: TypeAlias = NewType("int16", Annotated[int, Field(ge=-32768, le=32767)]) # type: ignore[type-arg] +int16 = NewType("int16", Annotated[int, Field(ge=-32768, le=32767)]) # type: ignore[type-arg] """ Portable 16-bit signed integer. @@ -63,7 +63,7 @@ portable across different serialization and validation platforms. """ -int32: TypeAlias = NewType("int32", Annotated[int, Field(ge=-(2**31), le=2**31 - 1)]) # type: ignore[type-arg] +int32 = NewType("int32", Annotated[int, Field(ge=-(2**31), le=2**31 - 1)]) # type: ignore[type-arg] """ Portable 32-bit signed integer. @@ -71,7 +71,7 @@ portable across different serialization and validation platforms. """ -int64: TypeAlias = NewType("int64", Annotated[int, Field(ge=-(2**63), le=2**63 - 1)]) # type: ignore[type-arg] +int64 = NewType("int64", Annotated[int, Field(ge=-(2**63), le=2**63 - 1)]) # type: ignore[type-arg] """ Portable 64-bit signed integer. @@ -79,7 +79,7 @@ portable across different serialization and validation platforms. """ -float32: TypeAlias = NewType("float32", float) # type: ignore[type-arg] +float32 = NewType("float32", float) # type: ignore[type-arg] """ Portable IEEE 32-bit floating point number. @@ -87,7 +87,7 @@ them portable across different serialization and validation platforms. """ -float64: TypeAlias = NewType("float64", float) # type: ignore[type-arg] +float64 = NewType("float64", float) # type: ignore[type-arg] """ Portable IEEE 64-bit floating point number. @@ -95,7 +95,7 @@ them portable across different serialization and validation platforms. """ -pct: TypeAlias = NewType("pct", Annotated[float, Field(ge=0, le=1)]) # type: ignore[type-arg] +pct = NewType("pct", Annotated[float, Field(ge=0, le=1)]) # type: ignore[type-arg] """ Portable percent value in the range [0, 1] where 0 represents 0% and 1 represents 100%. diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py b/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py deleted file mode 100644 index 8b1378917..000000000 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/num.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/string.py b/packages/overture-schema-foundation/src/overture/schema/foundation/string.py index 1b10c3484..6636cd1cb 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/string.py +++ b/packages/overture-schema-foundation/src/overture/schema/foundation/string.py @@ -1,4 +1,4 @@ -from typing import Annotated, NewType, TypeAlias +from typing import Annotated, NewType from pydantic import Field @@ -14,7 +14,7 @@ WikidataIdConstraint, ) -HexColor: TypeAlias = NewType( +HexColor = NewType( "HexColor", Annotated[ str, @@ -36,7 +36,7 @@ """ -JsonPointer: TypeAlias = NewType( +JsonPointer = NewType( "JsonPointer", Annotated[ str, @@ -52,7 +52,7 @@ """ -LanguageTag: TypeAlias = NewType( +LanguageTag = NewType( "LanguageTag", Annotated[ str, @@ -70,7 +70,7 @@ """ -StrippedString: TypeAlias = NewType( +StrippedString = NewType( "StrippedString", Annotated[ str, @@ -86,7 +86,7 @@ """ -NoWhitespaceString: TypeAlias = NewType( +NoWhitespaceString = NewType( "NoWhitespaceString", Annotated[ str, @@ -100,7 +100,7 @@ """ -CountryCode: TypeAlias = NewType( +CountryCode = NewType( "CountryCode", Annotated[ str, @@ -113,7 +113,7 @@ An ISO-316601 alpha-2 country code. """ -RegionCode: TypeAlias = NewType( +RegionCode = NewType( "RegionCode", Annotated[ str, @@ -126,7 +126,7 @@ An ISO 3166-2 principal subdivision code. """ -WikidataId: TypeAlias = NewType( +WikidataId = NewType( "WikidataId", Annotated[ str, @@ -140,7 +140,7 @@ """ -PhoneNumber: TypeAlias = NewType( +PhoneNumber = NewType( "PhoneNumber", Annotated[ str, PhoneNumberConstraint(), Field(description="An international phone number") From 520f3226624e3b1152b7393b4c41372f01081b22 Mon Sep 17 00:00:00 2001 From: schapper Date: Thu, 2 Oct 2025 16:46:18 -0700 Subject: [PATCH 17/20] wip - Rename 'foundation' package to 'system' to avoid OMF "Foundation" overload --- .../schema/addresses/address/models.py | 4 +- .../overture/schema/base/bathymetry/models.py | 2 +- .../schema/base/infrastructure/models.py | 2 +- .../src/overture/schema/base/land/models.py | 2 +- .../overture/schema/base/land_cover/models.py | 2 +- .../overture/schema/base/land_use/models.py | 2 +- .../src/overture/schema/base/models.py | 2 +- .../src/overture/schema/base/types.py | 2 +- .../src/overture/schema/base/water/models.py | 2 +- .../schema/buildings/building/models.py | 2 +- .../schema/buildings/building_part/models.py | 2 +- .../src/overture/schema/buildings/models.py | 4 +- .../src/overture/schema/core/models.py | 6 +- .../src/overture/schema/core/types.py | 6 +- .../src/overture/schema/core/validation.py | 2 +- .../test_json_schema_for_primitive_types.py | 2 +- .../overture-schema-core/tests/test_models.py | 2 +- .../tests/test_primitive_types.py | 2 +- .../schema/divisions/division/models.py | 6 +- .../schema/divisions/division_area/models.py | 4 +- .../divisions/division_boundary/models.py | 6 +- .../src/overture/schema/divisions/models.py | 2 +- .../src/overture/schema/divisions/types.py | 2 +- .../overture/schema/places/place/models.py | 6 +- .../README.md | 0 .../pyproject.toml | 0 .../src/overture/__init__.py | 0 .../src/overture/schema/__init__.py | 0 .../src/overture/schema/system}/__about__.py | 0 .../src/overture/schema/system}/__init__.py | 0 .../schema/system}/constraint/__init__.py | 0 .../schema/system}/constraint/collection.py | 2 +- .../schema/system}/constraint/constraint.py | 0 .../schema/system}/constraint/string.py | 2 +- .../schema/system}/primitive/__init__.py | 0 .../overture/schema/system}/primitive/bbox.py | 0 .../overture/schema/system}/primitive/geom.py | 0 .../src/overture/schema/system}/py.typed | 0 .../src/overture/schema/system}/string.py | 2 +- .../constraint/test_collection_constraints.py | 2 +- .../constraint/test_string_constraints.py | 2 +- .../tests/primitive/test_bbox.py | 2 +- .../tests/primitive/test_geometry.py | 2 +- .../tests/test_string.py | 2 +- .../schema/transportation/connector/models.py | 2 +- .../overture/schema/transportation/models.py | 6 +- .../schema/transportation/segment/models.py | 4 +- .../overture/schema/transportation/types.py | 2 +- .../tests/test_hashability.py | 2 +- .../overture/schema/validation/__init__.py | 2 +- .../overture/schema/validation/constraints.py | 4 +- .../src/overture/schema/validation/types.py | 2 +- .../tests/test_json_schema_generation.py | 8 +- uv.lock | 777 ++++++++++-------- 54 files changed, 490 insertions(+), 409 deletions(-) rename packages/{overture-schema-foundation => overture-schema-system}/README.md (100%) rename packages/{overture-schema-foundation => overture-schema-system}/pyproject.toml (100%) rename packages/{overture-schema-foundation => overture-schema-system}/src/overture/__init__.py (100%) rename packages/{overture-schema-foundation => overture-schema-system}/src/overture/schema/__init__.py (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/__about__.py (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/__init__.py (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/constraint/__init__.py (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/constraint/collection.py (97%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/constraint/constraint.py (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/constraint/string.py (99%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/primitive/__init__.py (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/primitive/bbox.py (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/primitive/geom.py (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/py.typed (100%) rename packages/{overture-schema-foundation/src/overture/schema/foundation => overture-schema-system/src/overture/schema/system}/string.py (98%) rename packages/{overture-schema-foundation => overture-schema-system}/tests/constraint/test_collection_constraints.py (96%) rename packages/{overture-schema-foundation => overture-schema-system}/tests/constraint/test_string_constraints.py (99%) rename packages/{overture-schema-foundation => overture-schema-system}/tests/primitive/test_bbox.py (98%) rename packages/{overture-schema-foundation => overture-schema-system}/tests/primitive/test_geometry.py (99%) rename packages/{overture-schema-foundation => overture-schema-system}/tests/test_string.py (97%) diff --git a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py index 0920af5d2..9bbfb22b8 100644 --- a/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py +++ b/packages/overture-schema-addresses-theme/src/overture/schema/addresses/address/models.py @@ -9,12 +9,12 @@ StrictBaseModel, ) from overture.schema.core.types import CountryCode -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, ) -from overture.schema.foundation.string import StrippedString +from overture.schema.system.string import StrippedString class AddressLevel(StrictBaseModel): diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py index 424100580..16a69e3f0 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/bathymetry/models.py @@ -9,7 +9,7 @@ Feature, ) from overture.schema.core.models import CartographicallyHinted -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py index aac7ef90d..9642920fb 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/infrastructure/models.py @@ -14,7 +14,7 @@ Feature, ) from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py index b27df789e..619680310 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land/models.py @@ -11,7 +11,7 @@ Feature, ) from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py index 8aae24cb3..d5d58f5ae 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land_cover/models.py @@ -9,7 +9,7 @@ Feature, ) from overture.schema.core.models import CartographicallyHinted -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py index 9f32326e4..a0c44dab6 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/land_use/models.py @@ -11,7 +11,7 @@ Feature, ) from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/models.py index 551c72ec4..f459d9760 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/models.py @@ -1,7 +1,7 @@ from pydantic import BaseModel from overture.schema.base.types import SourceTags -from overture.schema.foundation.string import WikidataId +from overture.schema.system.string import WikidataId class SourcedFromOpenStreetMap(BaseModel): diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/types.py b/packages/overture-schema-base-theme/src/overture/schema/base/types.py index 1698be4f1..05553387f 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/types.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/types.py @@ -2,7 +2,7 @@ from pydantic import Field -from overture.schema.foundation.primitive import float64, int32 +from overture.schema.system.primitive import float64, int32 Elevation = NewType( "Elevation", diff --git a/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py b/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py index ba18b0344..d88791676 100644 --- a/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py +++ b/packages/overture-schema-base-theme/src/overture/schema/base/water/models.py @@ -10,7 +10,7 @@ Feature, ) from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py index c392cd7e5..92024fac3 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py @@ -6,7 +6,7 @@ from overture.schema.core import Feature from overture.schema.core.models import Named, Stacked -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py index 8f25037b1..8878da463 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/building_part/models.py @@ -8,7 +8,7 @@ from overture.schema.core.models import Named, Stacked from overture.schema.core.ref import Reference, Relationship from overture.schema.core.types import Id -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py index e519eee89..059d1179c 100644 --- a/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py +++ b/packages/overture-schema-buildings-theme/src/overture/schema/buildings/models.py @@ -8,8 +8,8 @@ RoofOrientation, RoofShape, ) -from overture.schema.foundation.primitive import float64, int32 -from overture.schema.foundation.string import HexColor +from overture.schema.system.primitive import float64, int32 +from overture.schema.system.string import HexColor class Shape(BaseModel): diff --git a/packages/overture-schema-core/src/overture/schema/core/models.py b/packages/overture-schema-core/src/overture/schema/core/models.py index 03c9f5421..3c306ec99 100644 --- a/packages/overture-schema-core/src/overture/schema/core/models.py +++ b/packages/overture-schema-core/src/overture/schema/core/models.py @@ -12,12 +12,12 @@ ) from pydantic_core import core_schema -from overture.schema.foundation.constraint import UniqueItemsConstraint -from overture.schema.foundation.primitive import ( +from overture.schema.system.constraint import UniqueItemsConstraint +from overture.schema.system.primitive import ( BBox, Geometry, ) -from overture.schema.foundation.string import ( +from overture.schema.system.string import ( JsonPointer, LanguageTag, RegionCode, diff --git a/packages/overture-schema-core/src/overture/schema/core/types.py b/packages/overture-schema-core/src/overture/schema/core/types.py index e277d4f8e..08c66f358 100644 --- a/packages/overture-schema-core/src/overture/schema/core/types.py +++ b/packages/overture-schema-core/src/overture/schema/core/types.py @@ -3,11 +3,11 @@ from pydantic import Field -from overture.schema.foundation.constraint.string import ( +from overture.schema.system.constraint.string import ( CountryCodeConstraint, ) -from overture.schema.foundation.primitive import int32, pct -from overture.schema.foundation.string import ( +from overture.schema.system.primitive import int32, pct +from overture.schema.system.string import ( HexColor, JsonPointer, LanguageTag, diff --git a/packages/overture-schema-core/src/overture/schema/core/validation.py b/packages/overture-schema-core/src/overture/schema/core/validation.py index aaa254498..3b5f4e86a 100644 --- a/packages/overture-schema-core/src/overture/schema/core/validation.py +++ b/packages/overture-schema-core/src/overture/schema/core/validation.py @@ -1,4 +1,4 @@ -from overture.schema.foundation.constraint import UniqueItemsConstraint +from overture.schema.system.constraint import UniqueItemsConstraint from overture.schema.validation import ( ConstraintValidatedModel, any_of, diff --git a/packages/overture-schema-core/tests/test_json_schema_for_primitive_types.py b/packages/overture-schema-core/tests/test_json_schema_for_primitive_types.py index 51c535eaa..d8d115f2d 100644 --- a/packages/overture-schema-core/tests/test_json_schema_for_primitive_types.py +++ b/packages/overture-schema-core/tests/test_json_schema_for_primitive_types.py @@ -1,7 +1,7 @@ """Tests for JSON Schema generation with primitive types.""" from overture.schema.core.json_schema import json_schema -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( float32, float64, int32, diff --git a/packages/overture-schema-core/tests/test_models.py b/packages/overture-schema-core/tests/test_models.py index 3bda2ce9e..7af4b78a9 100644 --- a/packages/overture-schema-core/tests/test_models.py +++ b/packages/overture-schema-core/tests/test_models.py @@ -5,7 +5,7 @@ from deepdiff import DeepDiff from overture.schema.core.json_schema import EnhancedJsonSchemaGenerator from overture.schema.core.models import Feature -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( BBox, Geometry, ) diff --git a/packages/overture-schema-core/tests/test_primitive_types.py b/packages/overture-schema-core/tests/test_primitive_types.py index 5c3a04d1c..e35dc8ddd 100644 --- a/packages/overture-schema-core/tests/test_primitive_types.py +++ b/packages/overture-schema-core/tests/test_primitive_types.py @@ -3,7 +3,7 @@ from typing import Annotated import pytest -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( float32, float64, int32, diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py index dfa1f718f..0513d8d6e 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division/models.py @@ -20,16 +20,16 @@ CountryCode, Id, ) -from overture.schema.foundation.constraint import ( +from overture.schema.system.constraint import ( UniqueItemsConstraint, ) -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, int32, ) -from overture.schema.foundation.string import RegionCode, WikidataId +from overture.schema.system.string import RegionCode, WikidataId from ..enums import DivisionClass, PlaceType from ..models import CapitalOfDivisionItem diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py index 0017186e7..5ca0733ac 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_area/models.py @@ -16,12 +16,12 @@ from overture.schema.core.validation import ( exactly_one_of, ) -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, ) -from overture.schema.foundation.string import RegionCode +from overture.schema.system.string import RegionCode from ..division.models import Division from ..enums import PlaceType diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py index 5a246cf49..5f2875cd6 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary/models.py @@ -17,13 +17,13 @@ exactly_one_of, not_required_if, ) -from overture.schema.foundation.constraint import UniqueItemsConstraint -from overture.schema.foundation.primitive import ( +from overture.schema.system.constraint import UniqueItemsConstraint +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, ) -from overture.schema.foundation.string import RegionCode +from overture.schema.system.string import RegionCode from ..division import Division from ..enums import PlaceType diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py index fdd64efda..8dbca1673 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/models.py @@ -5,7 +5,7 @@ from overture.schema.core import StrictBaseModel from overture.schema.core.types import Id from overture.schema.divisions.enums import PlaceType -from overture.schema.foundation.string import StrippedString +from overture.schema.system.string import StrippedString DivisionId = NewType( "DivisionId", Annotated[Id, Field(min_length=1, description="ID of the division")] diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/types.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/types.py index 7bc79cff5..9bbff524b 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/types.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/types.py @@ -2,7 +2,7 @@ from pydantic import Field -from overture.schema.foundation.constraint import UniqueItemsConstraint +from overture.schema.system.constraint import UniqueItemsConstraint from .models import HierarchyItem diff --git a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py index 368641b38..cfc2e4e66 100644 --- a/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py +++ b/packages/overture-schema-places-theme/src/overture/schema/places/place/models.py @@ -15,15 +15,15 @@ from overture.schema.core.types import ( ConfidenceScore, ) -from overture.schema.foundation.constraint import ( +from overture.schema.system.constraint import ( UniqueItemsConstraint, ) -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, ) -from overture.schema.foundation.string import PhoneNumber, WikidataId +from overture.schema.system.string import PhoneNumber, WikidataId from ..types import PlaceCategory from .enums import OperatingStatus diff --git a/packages/overture-schema-foundation/README.md b/packages/overture-schema-system/README.md similarity index 100% rename from packages/overture-schema-foundation/README.md rename to packages/overture-schema-system/README.md diff --git a/packages/overture-schema-foundation/pyproject.toml b/packages/overture-schema-system/pyproject.toml similarity index 100% rename from packages/overture-schema-foundation/pyproject.toml rename to packages/overture-schema-system/pyproject.toml diff --git a/packages/overture-schema-foundation/src/overture/__init__.py b/packages/overture-schema-system/src/overture/__init__.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/__init__.py rename to packages/overture-schema-system/src/overture/__init__.py diff --git a/packages/overture-schema-foundation/src/overture/schema/__init__.py b/packages/overture-schema-system/src/overture/schema/__init__.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/__init__.py rename to packages/overture-schema-system/src/overture/schema/__init__.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/__about__.py b/packages/overture-schema-system/src/overture/schema/system/__about__.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/__about__.py rename to packages/overture-schema-system/src/overture/schema/system/__about__.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/__init__.py b/packages/overture-schema-system/src/overture/schema/system/__init__.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/__init__.py rename to packages/overture-schema-system/src/overture/schema/system/__init__.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py b/packages/overture-schema-system/src/overture/schema/system/constraint/__init__.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/constraint/__init__.py rename to packages/overture-schema-system/src/overture/schema/system/constraint/__init__.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/collection.py b/packages/overture-schema-system/src/overture/schema/system/constraint/collection.py similarity index 97% rename from packages/overture-schema-foundation/src/overture/schema/foundation/constraint/collection.py rename to packages/overture-schema-system/src/overture/schema/system/constraint/collection.py index 0396e702d..be6a99120 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/collection.py +++ b/packages/overture-schema-system/src/overture/schema/system/constraint/collection.py @@ -23,7 +23,7 @@ ) from pydantic_core import InitErrorDetails, core_schema -from overture.schema.foundation.constraint.constraint import Constraint +from overture.schema.system.constraint.constraint import Constraint class CollectionConstraint(Constraint): diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/constraint.py b/packages/overture-schema-system/src/overture/schema/system/constraint/constraint.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/constraint/constraint.py rename to packages/overture-schema-system/src/overture/schema/system/constraint/constraint.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/string.py b/packages/overture-schema-system/src/overture/schema/system/constraint/string.py similarity index 99% rename from packages/overture-schema-foundation/src/overture/schema/foundation/constraint/string.py rename to packages/overture-schema-system/src/overture/schema/system/constraint/string.py index 0a6479502..b2779bf31 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/constraint/string.py +++ b/packages/overture-schema-system/src/overture/schema/system/constraint/string.py @@ -9,7 +9,7 @@ ) from pydantic_core import InitErrorDetails, core_schema -from overture.schema.foundation.constraint import ( +from overture.schema.system.constraint import ( Constraint, ) diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py b/packages/overture-schema-system/src/overture/schema/system/primitive/__init__.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/primitive/__init__.py rename to packages/overture-schema-system/src/overture/schema/system/primitive/__init__.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/bbox.py b/packages/overture-schema-system/src/overture/schema/system/primitive/bbox.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/primitive/bbox.py rename to packages/overture-schema-system/src/overture/schema/system/primitive/bbox.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geom.py b/packages/overture-schema-system/src/overture/schema/system/primitive/geom.py similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/primitive/geom.py rename to packages/overture-schema-system/src/overture/schema/system/primitive/geom.py diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/py.typed b/packages/overture-schema-system/src/overture/schema/system/py.typed similarity index 100% rename from packages/overture-schema-foundation/src/overture/schema/foundation/py.typed rename to packages/overture-schema-system/src/overture/schema/system/py.typed diff --git a/packages/overture-schema-foundation/src/overture/schema/foundation/string.py b/packages/overture-schema-system/src/overture/schema/system/string.py similarity index 98% rename from packages/overture-schema-foundation/src/overture/schema/foundation/string.py rename to packages/overture-schema-system/src/overture/schema/system/string.py index 6636cd1cb..5c39eba31 100644 --- a/packages/overture-schema-foundation/src/overture/schema/foundation/string.py +++ b/packages/overture-schema-system/src/overture/schema/system/string.py @@ -2,7 +2,7 @@ from pydantic import Field -from overture.schema.foundation.constraint import ( +from overture.schema.system.constraint import ( CountryCodeConstraint, HexColorConstraint, JsonPointerConstraint, diff --git a/packages/overture-schema-foundation/tests/constraint/test_collection_constraints.py b/packages/overture-schema-system/tests/constraint/test_collection_constraints.py similarity index 96% rename from packages/overture-schema-foundation/tests/constraint/test_collection_constraints.py rename to packages/overture-schema-system/tests/constraint/test_collection_constraints.py index 1cfaa0137..196de68b3 100644 --- a/packages/overture-schema-foundation/tests/constraint/test_collection_constraints.py +++ b/packages/overture-schema-system/tests/constraint/test_collection_constraints.py @@ -3,7 +3,7 @@ import pytest from pydantic import BaseModel, Field, ValidationError -from overture.schema.foundation.constraint import UniqueItemsConstraint +from overture.schema.system.constraint import UniqueItemsConstraint class TestUniqueItemsConstraint: diff --git a/packages/overture-schema-foundation/tests/constraint/test_string_constraints.py b/packages/overture-schema-system/tests/constraint/test_string_constraints.py similarity index 99% rename from packages/overture-schema-foundation/tests/constraint/test_string_constraints.py rename to packages/overture-schema-system/tests/constraint/test_string_constraints.py index 91033d132..ebea26ca7 100644 --- a/packages/overture-schema-foundation/tests/constraint/test_string_constraints.py +++ b/packages/overture-schema-system/tests/constraint/test_string_constraints.py @@ -3,7 +3,7 @@ import pytest from pydantic import BaseModel, ValidationError -from overture.schema.foundation.constraint.string import ( +from overture.schema.system.constraint.string import ( CountryCodeConstraint, HexColorConstraint, JsonPointerConstraint, diff --git a/packages/overture-schema-foundation/tests/primitive/test_bbox.py b/packages/overture-schema-system/tests/primitive/test_bbox.py similarity index 98% rename from packages/overture-schema-foundation/tests/primitive/test_bbox.py rename to packages/overture-schema-system/tests/primitive/test_bbox.py index 68a6f3e36..530793bf5 100644 --- a/packages/overture-schema-foundation/tests/primitive/test_bbox.py +++ b/packages/overture-schema-system/tests/primitive/test_bbox.py @@ -4,7 +4,7 @@ import pytest from pydantic import BaseModel, ValidationError -from overture.schema.foundation.primitive.bbox import BBox +from overture.schema.system.primitive.bbox import BBox @pytest.mark.parametrize( diff --git a/packages/overture-schema-foundation/tests/primitive/test_geometry.py b/packages/overture-schema-system/tests/primitive/test_geometry.py similarity index 99% rename from packages/overture-schema-foundation/tests/primitive/test_geometry.py rename to packages/overture-schema-system/tests/primitive/test_geometry.py index 10c614cfc..c43f783e0 100644 --- a/packages/overture-schema-foundation/tests/primitive/test_geometry.py +++ b/packages/overture-schema-system/tests/primitive/test_geometry.py @@ -9,7 +9,7 @@ from pytest_subtests import SubTests from shapely import wkt -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-foundation/tests/test_string.py b/packages/overture-schema-system/tests/test_string.py similarity index 97% rename from packages/overture-schema-foundation/tests/test_string.py rename to packages/overture-schema-system/tests/test_string.py index 4ba4c58f8..e05a9c5fd 100644 --- a/packages/overture-schema-foundation/tests/test_string.py +++ b/packages/overture-schema-system/tests/test_string.py @@ -3,7 +3,7 @@ import pytest from pydantic import BaseModel, ValidationError -from overture.schema.foundation.string import ( +from overture.schema.system.string import ( CountryCode, HexColor, JsonPointer, diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py index 97c5a745d..c5c1d8882 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py @@ -7,7 +7,7 @@ from overture.schema.core import ( Feature, ) -from overture.schema.foundation.primitive import ( +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py index 94e89ec10..152903f9b 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py @@ -21,9 +21,9 @@ any_of, min_properties, ) -from overture.schema.foundation.constraint import UniqueItemsConstraint -from overture.schema.foundation.primitive import float64, int32 -from overture.schema.foundation.string import StrippedString, WikidataId +from overture.schema.system.constraint import UniqueItemsConstraint +from overture.schema.system.primitive import float64, int32 +from overture.schema.system.string import StrippedString, WikidataId from .enums import ( AccessType, diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py index 75e4bfeef..998a17b13 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py @@ -10,8 +10,8 @@ from overture.schema.core.models import ( Named, ) -from overture.schema.foundation.constraint import UniqueItemsConstraint -from overture.schema.foundation.primitive import ( +from overture.schema.system.constraint import UniqueItemsConstraint +from overture.schema.system.primitive import ( Geometry, GeometryType, GeometryTypeConstraint, diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py index 3f7080b7b..ca3078457 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py @@ -4,7 +4,7 @@ from pydantic import Field -from overture.schema.foundation.constraint import UniqueItemsConstraint +from overture.schema.system.constraint import UniqueItemsConstraint from .models import ( AccessRestrictionRule, diff --git a/packages/overture-schema-transportation-theme/tests/test_hashability.py b/packages/overture-schema-transportation-theme/tests/test_hashability.py index e3005fc6a..ab587d138 100644 --- a/packages/overture-schema-transportation-theme/tests/test_hashability.py +++ b/packages/overture-schema-transportation-theme/tests/test_hashability.py @@ -1,7 +1,7 @@ """Tests for hashability of all scope and related classes.""" from overture.schema.core.models import GeometricRangeScope -from overture.schema.foundation.primitive import Geometry +from overture.schema.system.primitive import Geometry from overture.schema.transportation.enums import ( AccessType, DestinationLabelType, diff --git a/packages/overture-schema-validation/src/overture/schema/validation/__init__.py b/packages/overture-schema-validation/src/overture/schema/validation/__init__.py index f5d7d9a16..104d78ebd 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/__init__.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/__init__.py @@ -27,7 +27,7 @@ class MyModel(BaseModel): tags: Annotated[list[str], UniqueItemsConstraint()] """ -from overture.schema.foundation.constraint import UniqueItemsConstraint +from overture.schema.system.constraint import UniqueItemsConstraint from .constraints import ( CategoryPatternConstraint, diff --git a/packages/overture-schema-validation/src/overture/schema/validation/constraints.py b/packages/overture-schema-validation/src/overture/schema/validation/constraints.py index 89b4cd344..dd1a2b6e5 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/constraints.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/constraints.py @@ -11,8 +11,8 @@ ) from pydantic_core import InitErrorDetails, core_schema -from overture.schema.foundation.constraint import CollectionConstraint, Constraint -from overture.schema.foundation.constraint.string import StringConstraint +from overture.schema.system.constraint import CollectionConstraint, Constraint +from overture.schema.system.constraint.string import StringConstraint class LinearReferenceRangeConstraint(CollectionConstraint): diff --git a/packages/overture-schema-validation/src/overture/schema/validation/types.py b/packages/overture-schema-validation/src/overture/schema/validation/types.py index 02d4d3618..a7fbff696 100644 --- a/packages/overture-schema-validation/src/overture/schema/validation/types.py +++ b/packages/overture-schema-validation/src/overture/schema/validation/types.py @@ -4,7 +4,7 @@ from pydantic import Field -from overture.schema.foundation.primitive import float64 +from overture.schema.system.primitive import float64 from .constraints import ( CategoryPatternConstraint, diff --git a/packages/overture-schema-validation/tests/test_json_schema_generation.py b/packages/overture-schema-validation/tests/test_json_schema_generation.py index 694965e2c..d6ea69ad8 100644 --- a/packages/overture-schema-validation/tests/test_json_schema_generation.py +++ b/packages/overture-schema-validation/tests/test_json_schema_generation.py @@ -4,10 +4,8 @@ from typing import Any import pytest -from pydantic import BaseModel, Field - -from overture.schema.foundation.constraint import UniqueItemsConstraint -from overture.schema.foundation.constraint.string import ( +from overture.schema.system.constraint import UniqueItemsConstraint +from overture.schema.system.constraint.string import ( CountryCodeConstraint, HexColorConstraint, JsonPointerConstraint, @@ -17,6 +15,8 @@ RegionCodeConstraint, StrippedConstraint, ) +from pydantic import BaseModel, Field + from overture.schema.validation.constraints import ( ConfidenceScoreConstraint, LinearReferenceRangeConstraint, diff --git a/uv.lock b/uv.lock index 24e17594d..6953f25fe 100644 --- a/uv.lock +++ b/uv.lock @@ -41,63 +41,66 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.2" +version = "3.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, - { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, - { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, - { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, - { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" }, + { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" }, + { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" }, + { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" }, + { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" }, + { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" }, + { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" }, + { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" }, + { url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825, upload-time = "2025-08-09T07:55:50.305Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452, upload-time = "2025-08-09T07:55:51.461Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" }, + { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" }, + { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" }, + { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" }, + { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" }, + { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" }, + { url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616, upload-time = "2025-08-09T07:56:05.658Z" }, + { url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108, upload-time = "2025-08-09T07:56:07.176Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, + { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, + { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, + { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, + { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, + { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, + { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, + { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" }, + { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" }, + { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" }, + { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" }, + { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" }, + { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" }, + { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" }, + { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" }, + { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" }, + { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" }, + { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" }, + { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" }, + { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" }, + { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" }, + { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, ] [[package]] @@ -111,66 +114,101 @@ wheels = [ [[package]] name = "coverage" -version = "7.9.2" +version = "7.10.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/b7/c0465ca253df10a9e8dae0692a4ae6e9726d245390aaef92360e1d6d3832/coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b", size = 813556, upload-time = "2025-07-03T10:54:15.101Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", size = 827704, upload-time = "2025-09-21T20:03:56.815Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/0d/5c2114fd776c207bd55068ae8dc1bef63ecd1b767b3389984a8e58f2b926/coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912", size = 212039, upload-time = "2025-07-03T10:52:38.955Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ad/dc51f40492dc2d5fcd31bb44577bc0cc8920757d6bc5d3e4293146524ef9/coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f", size = 212428, upload-time = "2025-07-03T10:52:41.36Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a3/55cb3ff1b36f00df04439c3993d8529193cdf165a2467bf1402539070f16/coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f", size = 241534, upload-time = "2025-07-03T10:52:42.956Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c9/a8410b91b6be4f6e9c2e9f0dce93749b6b40b751d7065b4410bf89cb654b/coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf", size = 239408, upload-time = "2025-07-03T10:52:44.199Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c4/6f3e56d467c612b9070ae71d5d3b114c0b899b5788e1ca3c93068ccb7018/coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547", size = 240552, upload-time = "2025-07-03T10:52:45.477Z" }, - { url = "https://files.pythonhosted.org/packages/fd/20/04eda789d15af1ce79bce5cc5fd64057c3a0ac08fd0576377a3096c24663/coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45", size = 240464, upload-time = "2025-07-03T10:52:46.809Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5a/217b32c94cc1a0b90f253514815332d08ec0812194a1ce9cca97dda1cd20/coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2", size = 239134, upload-time = "2025-07-03T10:52:48.149Z" }, - { url = "https://files.pythonhosted.org/packages/34/73/1d019c48f413465eb5d3b6898b6279e87141c80049f7dbf73fd020138549/coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e", size = 239405, upload-time = "2025-07-03T10:52:49.687Z" }, - { url = "https://files.pythonhosted.org/packages/49/6c/a2beca7aa2595dad0c0d3f350382c381c92400efe5261e2631f734a0e3fe/coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e", size = 214519, upload-time = "2025-07-03T10:52:51.036Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c8/91e5e4a21f9a51e2c7cdd86e587ae01a4fcff06fc3fa8cde4d6f7cf68df4/coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c", size = 215400, upload-time = "2025-07-03T10:52:52.313Z" }, - { url = "https://files.pythonhosted.org/packages/39/40/916786453bcfafa4c788abee4ccd6f592b5b5eca0cd61a32a4e5a7ef6e02/coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba", size = 212152, upload-time = "2025-07-03T10:52:53.562Z" }, - { url = "https://files.pythonhosted.org/packages/9f/66/cc13bae303284b546a030762957322bbbff1ee6b6cb8dc70a40f8a78512f/coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa", size = 212540, upload-time = "2025-07-03T10:52:55.196Z" }, - { url = "https://files.pythonhosted.org/packages/0f/3c/d56a764b2e5a3d43257c36af4a62c379df44636817bb5f89265de4bf8bd7/coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a", size = 245097, upload-time = "2025-07-03T10:52:56.509Z" }, - { url = "https://files.pythonhosted.org/packages/b1/46/bd064ea8b3c94eb4ca5d90e34d15b806cba091ffb2b8e89a0d7066c45791/coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc", size = 242812, upload-time = "2025-07-03T10:52:57.842Z" }, - { url = "https://files.pythonhosted.org/packages/43/02/d91992c2b29bc7afb729463bc918ebe5f361be7f1daae93375a5759d1e28/coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2", size = 244617, upload-time = "2025-07-03T10:52:59.239Z" }, - { url = "https://files.pythonhosted.org/packages/b7/4f/8fadff6bf56595a16d2d6e33415841b0163ac660873ed9a4e9046194f779/coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c", size = 244263, upload-time = "2025-07-03T10:53:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d2/e0be7446a2bba11739edb9f9ba4eff30b30d8257370e237418eb44a14d11/coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd", size = 242314, upload-time = "2025-07-03T10:53:01.932Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7d/dcbac9345000121b8b57a3094c2dfcf1ccc52d8a14a40c1d4bc89f936f80/coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74", size = 242904, upload-time = "2025-07-03T10:53:03.478Z" }, - { url = "https://files.pythonhosted.org/packages/41/58/11e8db0a0c0510cf31bbbdc8caf5d74a358b696302a45948d7c768dfd1cf/coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6", size = 214553, upload-time = "2025-07-03T10:53:05.174Z" }, - { url = "https://files.pythonhosted.org/packages/3a/7d/751794ec8907a15e257136e48dc1021b1f671220ecccfd6c4eaf30802714/coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7", size = 215441, upload-time = "2025-07-03T10:53:06.472Z" }, - { url = "https://files.pythonhosted.org/packages/62/5b/34abcedf7b946c1c9e15b44f326cb5b0da852885312b30e916f674913428/coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62", size = 213873, upload-time = "2025-07-03T10:53:07.699Z" }, - { url = "https://files.pythonhosted.org/packages/53/d7/7deefc6fd4f0f1d4c58051f4004e366afc9e7ab60217ac393f247a1de70a/coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0", size = 212344, upload-time = "2025-07-03T10:53:09.3Z" }, - { url = "https://files.pythonhosted.org/packages/95/0c/ee03c95d32be4d519e6a02e601267769ce2e9a91fc8faa1b540e3626c680/coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3", size = 212580, upload-time = "2025-07-03T10:53:11.52Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9f/826fa4b544b27620086211b87a52ca67592622e1f3af9e0a62c87aea153a/coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1", size = 246383, upload-time = "2025-07-03T10:53:13.134Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b3/4477aafe2a546427b58b9c540665feff874f4db651f4d3cb21b308b3a6d2/coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615", size = 243400, upload-time = "2025-07-03T10:53:14.614Z" }, - { url = "https://files.pythonhosted.org/packages/f8/c2/efffa43778490c226d9d434827702f2dfbc8041d79101a795f11cbb2cf1e/coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b", size = 245591, upload-time = "2025-07-03T10:53:15.872Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e7/a59888e882c9a5f0192d8627a30ae57910d5d449c80229b55e7643c078c4/coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9", size = 245402, upload-time = "2025-07-03T10:53:17.124Z" }, - { url = "https://files.pythonhosted.org/packages/92/a5/72fcd653ae3d214927edc100ce67440ed8a0a1e3576b8d5e6d066ed239db/coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f", size = 243583, upload-time = "2025-07-03T10:53:18.781Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f5/84e70e4df28f4a131d580d7d510aa1ffd95037293da66fd20d446090a13b/coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d", size = 244815, upload-time = "2025-07-03T10:53:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/39/e7/d73d7cbdbd09fdcf4642655ae843ad403d9cbda55d725721965f3580a314/coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355", size = 214719, upload-time = "2025-07-03T10:53:21.521Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d6/7486dcc3474e2e6ad26a2af2db7e7c162ccd889c4c68fa14ea8ec189c9e9/coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0", size = 215509, upload-time = "2025-07-03T10:53:22.853Z" }, - { url = "https://files.pythonhosted.org/packages/b7/34/0439f1ae2593b0346164d907cdf96a529b40b7721a45fdcf8b03c95fcd90/coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b", size = 213910, upload-time = "2025-07-03T10:53:24.472Z" }, - { url = "https://files.pythonhosted.org/packages/94/9d/7a8edf7acbcaa5e5c489a646226bed9591ee1c5e6a84733c0140e9ce1ae1/coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038", size = 212367, upload-time = "2025-07-03T10:53:25.811Z" }, - { url = "https://files.pythonhosted.org/packages/e8/9e/5cd6f130150712301f7e40fb5865c1bc27b97689ec57297e568d972eec3c/coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d", size = 212632, upload-time = "2025-07-03T10:53:27.075Z" }, - { url = "https://files.pythonhosted.org/packages/a8/de/6287a2c2036f9fd991c61cefa8c64e57390e30c894ad3aa52fac4c1e14a8/coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3", size = 245793, upload-time = "2025-07-03T10:53:28.408Z" }, - { url = "https://files.pythonhosted.org/packages/06/cc/9b5a9961d8160e3cb0b558c71f8051fe08aa2dd4b502ee937225da564ed1/coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14", size = 243006, upload-time = "2025-07-03T10:53:29.754Z" }, - { url = "https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6", size = 244990, upload-time = "2025-07-03T10:53:31.098Z" }, - { url = "https://files.pythonhosted.org/packages/48/83/801cdc10f137b2d02b005a761661649ffa60eb173dcdaeb77f571e4dc192/coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b", size = 245157, upload-time = "2025-07-03T10:53:32.717Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a4/41911ed7e9d3ceb0ffb019e7635468df7499f5cc3edca5f7dfc078e9c5ec/coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d", size = 243128, upload-time = "2025-07-03T10:53:34.009Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/344543b71d31ac9cb00a664d5d0c9ef134a0fe87cb7d8430003b20fa0b7d/coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868", size = 244511, upload-time = "2025-07-03T10:53:35.434Z" }, - { url = "https://files.pythonhosted.org/packages/d5/81/3b68c77e4812105e2a060f6946ba9e6f898ddcdc0d2bfc8b4b152a9ae522/coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a", size = 214765, upload-time = "2025-07-03T10:53:36.787Z" }, - { url = "https://files.pythonhosted.org/packages/06/a2/7fac400f6a346bb1a4004eb2a76fbff0e242cd48926a2ce37a22a6a1d917/coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b", size = 215536, upload-time = "2025-07-03T10:53:38.188Z" }, - { url = "https://files.pythonhosted.org/packages/08/47/2c6c215452b4f90d87017e61ea0fd9e0486bb734cb515e3de56e2c32075f/coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694", size = 213943, upload-time = "2025-07-03T10:53:39.492Z" }, - { url = "https://files.pythonhosted.org/packages/a3/46/e211e942b22d6af5e0f323faa8a9bc7c447a1cf1923b64c47523f36ed488/coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5", size = 213088, upload-time = "2025-07-03T10:53:40.874Z" }, - { url = "https://files.pythonhosted.org/packages/d2/2f/762551f97e124442eccd907bf8b0de54348635b8866a73567eb4e6417acf/coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b", size = 213298, upload-time = "2025-07-03T10:53:42.218Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b7/76d2d132b7baf7360ed69be0bcab968f151fa31abe6d067f0384439d9edb/coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3", size = 256541, upload-time = "2025-07-03T10:53:43.823Z" }, - { url = "https://files.pythonhosted.org/packages/a0/17/392b219837d7ad47d8e5974ce5f8dc3deb9f99a53b3bd4d123602f960c81/coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8", size = 252761, upload-time = "2025-07-03T10:53:45.19Z" }, - { url = "https://files.pythonhosted.org/packages/d5/77/4256d3577fe1b0daa8d3836a1ebe68eaa07dd2cbaf20cf5ab1115d6949d4/coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46", size = 254917, upload-time = "2025-07-03T10:53:46.931Z" }, - { url = "https://files.pythonhosted.org/packages/53/99/fc1a008eef1805e1ddb123cf17af864743354479ea5129a8f838c433cc2c/coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584", size = 256147, upload-time = "2025-07-03T10:53:48.289Z" }, - { url = "https://files.pythonhosted.org/packages/92/c0/f63bf667e18b7f88c2bdb3160870e277c4874ced87e21426128d70aa741f/coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e", size = 254261, upload-time = "2025-07-03T10:53:49.99Z" }, - { url = "https://files.pythonhosted.org/packages/8c/32/37dd1c42ce3016ff8ec9e4b607650d2e34845c0585d3518b2a93b4830c1a/coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac", size = 255099, upload-time = "2025-07-03T10:53:51.354Z" }, - { url = "https://files.pythonhosted.org/packages/da/2e/af6b86f7c95441ce82f035b3affe1cd147f727bbd92f563be35e2d585683/coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926", size = 215440, upload-time = "2025-07-03T10:53:52.808Z" }, - { url = "https://files.pythonhosted.org/packages/4d/bb/8a785d91b308867f6b2e36e41c569b367c00b70c17f54b13ac29bcd2d8c8/coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd", size = 216537, upload-time = "2025-07-03T10:53:54.273Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a0/a6bffb5e0f41a47279fd45a8f3155bf193f77990ae1c30f9c224b61cacb0/coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb", size = 214398, upload-time = "2025-07-03T10:53:56.715Z" }, - { url = "https://files.pythonhosted.org/packages/d7/85/f8bbefac27d286386961c25515431482a425967e23d3698b75a250872924/coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050", size = 204013, upload-time = "2025-07-03T10:54:12.084Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" }, + { url = "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", size = 217987, upload-time = "2025-09-21T20:00:57.218Z" }, + { url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", size = 218388, upload-time = "2025-09-21T20:01:00.081Z" }, + { url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", size = 245148, upload-time = "2025-09-21T20:01:01.768Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", size = 246958, upload-time = "2025-09-21T20:01:03.355Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", size = 248819, upload-time = "2025-09-21T20:01:04.968Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", size = 245754, upload-time = "2025-09-21T20:01:06.321Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", size = 246860, upload-time = "2025-09-21T20:01:07.605Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", size = 244877, upload-time = "2025-09-21T20:01:08.829Z" }, + { url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", size = 245108, upload-time = "2025-09-21T20:01:10.527Z" }, + { url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", size = 245752, upload-time = "2025-09-21T20:01:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b", size = 220497, upload-time = "2025-09-21T20:01:13.459Z" }, + { url = "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807", size = 221392, upload-time = "2025-09-21T20:01:14.722Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", size = 218102, upload-time = "2025-09-21T20:01:16.089Z" }, + { url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", size = 218505, upload-time = "2025-09-21T20:01:17.788Z" }, + { url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", size = 248898, upload-time = "2025-09-21T20:01:19.488Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", size = 250831, upload-time = "2025-09-21T20:01:20.817Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", size = 252937, upload-time = "2025-09-21T20:01:22.171Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", size = 249021, upload-time = "2025-09-21T20:01:23.907Z" }, + { url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", size = 250626, upload-time = "2025-09-21T20:01:25.721Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", size = 248682, upload-time = "2025-09-21T20:01:27.105Z" }, + { url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", size = 248402, upload-time = "2025-09-21T20:01:28.629Z" }, + { url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", size = 249320, upload-time = "2025-09-21T20:01:30.004Z" }, + { url = "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14", size = 220536, upload-time = "2025-09-21T20:01:32.184Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2", size = 221425, upload-time = "2025-09-21T20:01:33.557Z" }, + { url = "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a", size = 220103, upload-time = "2025-09-21T20:01:34.929Z" }, + { url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", size = 218290, upload-time = "2025-09-21T20:01:36.455Z" }, + { url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", size = 218515, upload-time = "2025-09-21T20:01:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", size = 250020, upload-time = "2025-09-21T20:01:39.617Z" }, + { url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", size = 252769, upload-time = "2025-09-21T20:01:41.341Z" }, + { url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", size = 253901, upload-time = "2025-09-21T20:01:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", size = 250413, upload-time = "2025-09-21T20:01:44.469Z" }, + { url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", size = 251820, upload-time = "2025-09-21T20:01:45.915Z" }, + { url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", size = 249941, upload-time = "2025-09-21T20:01:47.296Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", size = 249519, upload-time = "2025-09-21T20:01:48.73Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", size = 251375, upload-time = "2025-09-21T20:01:50.529Z" }, + { url = "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256", size = 220699, upload-time = "2025-09-21T20:01:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba", size = 221512, upload-time = "2025-09-21T20:01:53.481Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf", size = 220147, upload-time = "2025-09-21T20:01:55.2Z" }, + { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", size = 218320, upload-time = "2025-09-21T20:01:56.629Z" }, + { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", size = 218575, upload-time = "2025-09-21T20:01:58.203Z" }, + { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", size = 249568, upload-time = "2025-09-21T20:01:59.748Z" }, + { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", size = 252174, upload-time = "2025-09-21T20:02:01.192Z" }, + { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", size = 253447, upload-time = "2025-09-21T20:02:02.701Z" }, + { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", size = 249779, upload-time = "2025-09-21T20:02:04.185Z" }, + { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", size = 251604, upload-time = "2025-09-21T20:02:06.034Z" }, + { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", size = 249497, upload-time = "2025-09-21T20:02:07.619Z" }, + { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", size = 249350, upload-time = "2025-09-21T20:02:10.34Z" }, + { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", size = 251111, upload-time = "2025-09-21T20:02:12.122Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", size = 220746, upload-time = "2025-09-21T20:02:13.919Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", size = 221541, upload-time = "2025-09-21T20:02:15.57Z" }, + { url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", size = 220170, upload-time = "2025-09-21T20:02:17.395Z" }, + { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", size = 219029, upload-time = "2025-09-21T20:02:18.936Z" }, + { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", size = 219259, upload-time = "2025-09-21T20:02:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", size = 260592, upload-time = "2025-09-21T20:02:22.313Z" }, + { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", size = 262768, upload-time = "2025-09-21T20:02:24.287Z" }, + { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", size = 264995, upload-time = "2025-09-21T20:02:26.133Z" }, + { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", size = 259546, upload-time = "2025-09-21T20:02:27.716Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", size = 262544, upload-time = "2025-09-21T20:02:29.216Z" }, + { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", size = 260308, upload-time = "2025-09-21T20:02:31.226Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", size = 258920, upload-time = "2025-09-21T20:02:32.823Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", size = 261434, upload-time = "2025-09-21T20:02:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", size = 221403, upload-time = "2025-09-21T20:02:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", size = 222469, upload-time = "2025-09-21T20:02:39.011Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", size = 220731, upload-time = "2025-09-21T20:02:40.939Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, + { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, + { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, + { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", size = 221009, upload-time = "2025-09-21T20:03:01.324Z" }, + { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", size = 221804, upload-time = "2025-09-21T20:03:03.4Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", size = 220384, upload-time = "2025-09-21T20:03:05.111Z" }, + { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, + { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, + { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, + { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, + { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, + { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, + { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, + { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, ] [package.optional-dependencies] @@ -180,23 +218,23 @@ toml = [ [[package]] name = "deepdiff" -version = "8.5.0" +version = "8.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "orderly-set" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/0f/9cd2624f7dcd755cbf1fa21fb7234541f19a1be96a56f387ec9053ebe220/deepdiff-8.5.0.tar.gz", hash = "sha256:a4dd3529fa8d4cd5b9cbb6e3ea9c95997eaa919ba37dac3966c1b8f872dc1cd1", size = 538517, upload-time = "2025-05-09T18:44:10.035Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/76/36c9aab3d5c19a94091f7c6c6e784efca50d87b124bf026c36e94719f33c/deepdiff-8.6.1.tar.gz", hash = "sha256:ec56d7a769ca80891b5200ec7bd41eec300ced91ebcc7797b41eb2b3f3ff643a", size = 634054, upload-time = "2025-09-03T19:40:41.461Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/3b/2e0797200c51531a6d8c97a8e4c9fa6fb56de7e6e2a15c1c067b6b10a0b0/deepdiff-8.5.0-py3-none-any.whl", hash = "sha256:d4599db637f36a1c285f5fdfc2cd8d38bde8d8be8636b65ab5e425b67c54df26", size = 85112, upload-time = "2025-05-09T18:44:07.784Z" }, + { url = "https://files.pythonhosted.org/packages/f7/e6/efe534ef0952b531b630780e19cabd416e2032697019d5295defc6ef9bd9/deepdiff-8.6.1-py3-none-any.whl", hash = "sha256:ee8708a7f7d37fb273a541fa24ad010ed484192cd0c4ffc0fa0ed5e2d4b9e78b", size = 91378, upload-time = "2025-09-03T19:40:39.679Z" }, ] [[package]] name = "dnspython" -version = "2.7.0" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] [[package]] @@ -214,15 +252,15 @@ wheels = [ [[package]] name = "email-validator" -version = "2.2.0" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967, upload-time = "2024-06-20T11:30:30.034Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521, upload-time = "2024-06-20T11:30:28.248Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] [[package]] @@ -230,7 +268,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ @@ -366,7 +404,7 @@ wheels = [ [[package]] name = "mypy" -version = "1.17.0" +version = "1.18.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, @@ -374,33 +412,39 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" }, - { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" }, - { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" }, - { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, - { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, - { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, - { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, - { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, - { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, - { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, - { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, - { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, - { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, - { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, - { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, - { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, - { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, - { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, - { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, + { url = "https://files.pythonhosted.org/packages/03/6f/657961a0743cff32e6c0611b63ff1c1970a0b482ace35b069203bf705187/mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c", size = 12807973, upload-time = "2025-09-19T00:10:35.282Z" }, + { url = "https://files.pythonhosted.org/packages/10/e9/420822d4f661f13ca8900f5fa239b40ee3be8b62b32f3357df9a3045a08b/mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e", size = 11896527, upload-time = "2025-09-19T00:10:55.791Z" }, + { url = "https://files.pythonhosted.org/packages/aa/73/a05b2bbaa7005f4642fcfe40fb73f2b4fb6bb44229bd585b5878e9a87ef8/mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b", size = 12507004, upload-time = "2025-09-19T00:11:05.411Z" }, + { url = "https://files.pythonhosted.org/packages/4f/01/f6e4b9f0d031c11ccbd6f17da26564f3a0f3c4155af344006434b0a05a9d/mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66", size = 13245947, upload-time = "2025-09-19T00:10:46.923Z" }, + { url = "https://files.pythonhosted.org/packages/d7/97/19727e7499bfa1ae0773d06afd30ac66a58ed7437d940c70548634b24185/mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428", size = 13499217, upload-time = "2025-09-19T00:09:39.472Z" }, + { url = "https://files.pythonhosted.org/packages/9f/4f/90dc8c15c1441bf31cf0f9918bb077e452618708199e530f4cbd5cede6ff/mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed", size = 9766753, upload-time = "2025-09-19T00:10:49.161Z" }, + { url = "https://files.pythonhosted.org/packages/88/87/cafd3ae563f88f94eec33f35ff722d043e09832ea8530ef149ec1efbaf08/mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f", size = 12731198, upload-time = "2025-09-19T00:09:44.857Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e0/1e96c3d4266a06d4b0197ace5356d67d937d8358e2ee3ffac71faa843724/mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341", size = 11817879, upload-time = "2025-09-19T00:09:47.131Z" }, + { url = "https://files.pythonhosted.org/packages/72/ef/0c9ba89eb03453e76bdac5a78b08260a848c7bfc5d6603634774d9cd9525/mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d", size = 12427292, upload-time = "2025-09-19T00:10:22.472Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/ec4a061dd599eb8179d5411d99775bec2a20542505988f40fc2fee781068/mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86", size = 13163750, upload-time = "2025-09-19T00:09:51.472Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5f/2cf2ceb3b36372d51568f2208c021870fe7834cf3186b653ac6446511839/mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37", size = 13351827, upload-time = "2025-09-19T00:09:58.311Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/2697b930179e7277529eaaec1513f8de622818696857f689e4a5432e5e27/mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8", size = 9757983, upload-time = "2025-09-19T00:10:09.071Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" }, + { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" }, + { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" }, + { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" }, + { url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" }, + { url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" }, + { url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" }, + { url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" }, + { url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" }, + { url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775, upload-time = "2025-09-19T00:10:03.814Z" }, + { url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852, upload-time = "2025-09-19T00:10:51.631Z" }, + { url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242, upload-time = "2025-09-19T00:11:07.955Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683, upload-time = "2025-09-19T00:09:55.572Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749, upload-time = "2025-09-19T00:10:44.827Z" }, + { url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959, upload-time = "2025-09-19T00:10:37.344Z" }, + { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" }, ] [[package]] @@ -479,86 +523,86 @@ wheels = [ [[package]] name = "numpy" -version = "2.3.2" +version = "2.3.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", ] -sdist = { url = "https://files.pythonhosted.org/packages/37/7d/3fec4199c5ffb892bed55cff901e4f39a58c81df9c44c280499e92cad264/numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48", size = 20489306, upload-time = "2025-07-24T21:32:07.553Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/26/1320083986108998bd487e2931eed2aeedf914b6e8905431487543ec911d/numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9", size = 21259016, upload-time = "2025-07-24T20:24:35.214Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2b/792b341463fa93fc7e55abbdbe87dac316c5b8cb5e94fb7a59fb6fa0cda5/numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168", size = 14451158, upload-time = "2025-07-24T20:24:58.397Z" }, - { url = "https://files.pythonhosted.org/packages/b7/13/e792d7209261afb0c9f4759ffef6135b35c77c6349a151f488f531d13595/numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b", size = 5379817, upload-time = "2025-07-24T20:25:07.746Z" }, - { url = "https://files.pythonhosted.org/packages/49/ce/055274fcba4107c022b2113a213c7287346563f48d62e8d2a5176ad93217/numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8", size = 6913606, upload-time = "2025-07-24T20:25:18.84Z" }, - { url = "https://files.pythonhosted.org/packages/17/f2/e4d72e6bc5ff01e2ab613dc198d560714971900c03674b41947e38606502/numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d", size = 14589652, upload-time = "2025-07-24T20:25:40.356Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b0/fbeee3000a51ebf7222016e2939b5c5ecf8000a19555d04a18f1e02521b8/numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3", size = 16938816, upload-time = "2025-07-24T20:26:05.721Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ec/2f6c45c3484cc159621ea8fc000ac5a86f1575f090cac78ac27193ce82cd/numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f", size = 16370512, upload-time = "2025-07-24T20:26:30.545Z" }, - { url = "https://files.pythonhosted.org/packages/b5/01/dd67cf511850bd7aefd6347aaae0956ed415abea741ae107834aae7d6d4e/numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097", size = 18884947, upload-time = "2025-07-24T20:26:58.24Z" }, - { url = "https://files.pythonhosted.org/packages/a7/17/2cf60fd3e6a61d006778735edf67a222787a8c1a7842aed43ef96d777446/numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220", size = 6599494, upload-time = "2025-07-24T20:27:09.786Z" }, - { url = "https://files.pythonhosted.org/packages/d5/03/0eade211c504bda872a594f045f98ddcc6caef2b7c63610946845e304d3f/numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170", size = 13087889, upload-time = "2025-07-24T20:27:29.558Z" }, - { url = "https://files.pythonhosted.org/packages/13/32/2c7979d39dafb2a25087e12310fc7f3b9d3c7d960df4f4bc97955ae0ce1d/numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89", size = 10459560, upload-time = "2025-07-24T20:27:46.803Z" }, - { url = "https://files.pythonhosted.org/packages/00/6d/745dd1c1c5c284d17725e5c802ca4d45cfc6803519d777f087b71c9f4069/numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b", size = 20956420, upload-time = "2025-07-24T20:28:18.002Z" }, - { url = "https://files.pythonhosted.org/packages/bc/96/e7b533ea5740641dd62b07a790af5d9d8fec36000b8e2d0472bd7574105f/numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f", size = 14184660, upload-time = "2025-07-24T20:28:39.522Z" }, - { url = "https://files.pythonhosted.org/packages/2b/53/102c6122db45a62aa20d1b18c9986f67e6b97e0d6fbc1ae13e3e4c84430c/numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0", size = 5113382, upload-time = "2025-07-24T20:28:48.544Z" }, - { url = "https://files.pythonhosted.org/packages/2b/21/376257efcbf63e624250717e82b4fae93d60178f09eb03ed766dbb48ec9c/numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b", size = 6647258, upload-time = "2025-07-24T20:28:59.104Z" }, - { url = "https://files.pythonhosted.org/packages/91/ba/f4ebf257f08affa464fe6036e13f2bf9d4642a40228781dc1235da81be9f/numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370", size = 14281409, upload-time = "2025-07-24T20:40:30.298Z" }, - { url = "https://files.pythonhosted.org/packages/59/ef/f96536f1df42c668cbacb727a8c6da7afc9c05ece6d558927fb1722693e1/numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73", size = 16641317, upload-time = "2025-07-24T20:40:56.625Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a7/af813a7b4f9a42f498dde8a4c6fcbff8100eed00182cc91dbaf095645f38/numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc", size = 16056262, upload-time = "2025-07-24T20:41:20.797Z" }, - { url = "https://files.pythonhosted.org/packages/8b/5d/41c4ef8404caaa7f05ed1cfb06afe16a25895260eacbd29b4d84dff2920b/numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be", size = 18579342, upload-time = "2025-07-24T20:41:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/9950e44c5a11636f4a3af6e825ec23003475cc9a466edb7a759ed3ea63bd/numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036", size = 6320610, upload-time = "2025-07-24T20:42:01.551Z" }, - { url = "https://files.pythonhosted.org/packages/7c/2f/244643a5ce54a94f0a9a2ab578189c061e4a87c002e037b0829dd77293b6/numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f", size = 12786292, upload-time = "2025-07-24T20:42:20.738Z" }, - { url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c0/c6bb172c916b00700ed3bf71cb56175fd1f7dbecebf8353545d0b5519f6c/numpy-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c8d9727f5316a256425892b043736d63e89ed15bbfe6556c5ff4d9d4448ff3b3", size = 20949074, upload-time = "2025-07-24T20:43:07.813Z" }, - { url = "https://files.pythonhosted.org/packages/20/4e/c116466d22acaf4573e58421c956c6076dc526e24a6be0903219775d862e/numpy-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:efc81393f25f14d11c9d161e46e6ee348637c0a1e8a54bf9dedc472a3fae993b", size = 14177311, upload-time = "2025-07-24T20:43:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/78/45/d4698c182895af189c463fc91d70805d455a227261d950e4e0f1310c2550/numpy-2.3.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dd937f088a2df683cbb79dda9a772b62a3e5a8a7e76690612c2737f38c6ef1b6", size = 5106022, upload-time = "2025-07-24T20:43:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/9f/76/3e6880fef4420179309dba72a8c11f6166c431cf6dee54c577af8906f914/numpy-2.3.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:11e58218c0c46c80509186e460d79fbdc9ca1eb8d8aee39d8f2dc768eb781089", size = 6640135, upload-time = "2025-07-24T20:43:49.28Z" }, - { url = "https://files.pythonhosted.org/packages/34/fa/87ff7f25b3c4ce9085a62554460b7db686fef1e0207e8977795c7b7d7ba1/numpy-2.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ad4ebcb683a1f99f4f392cc522ee20a18b2bb12a2c1c42c3d48d5a1adc9d3d2", size = 14278147, upload-time = "2025-07-24T20:44:10.328Z" }, - { url = "https://files.pythonhosted.org/packages/1d/0f/571b2c7a3833ae419fe69ff7b479a78d313581785203cc70a8db90121b9a/numpy-2.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f", size = 16635989, upload-time = "2025-07-24T20:44:34.88Z" }, - { url = "https://files.pythonhosted.org/packages/24/5a/84ae8dca9c9a4c592fe11340b36a86ffa9fd3e40513198daf8a97839345c/numpy-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66459dccc65d8ec98cc7df61307b64bf9e08101f9598755d42d8ae65d9a7a6ee", size = 16053052, upload-time = "2025-07-24T20:44:58.872Z" }, - { url = "https://files.pythonhosted.org/packages/57/7c/e5725d99a9133b9813fcf148d3f858df98511686e853169dbaf63aec6097/numpy-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7af9ed2aa9ec5950daf05bb11abc4076a108bd3c7db9aa7251d5f107079b6a6", size = 18577955, upload-time = "2025-07-24T20:45:26.714Z" }, - { url = "https://files.pythonhosted.org/packages/ae/11/7c546fcf42145f29b71e4d6f429e96d8d68e5a7ba1830b2e68d7418f0bbd/numpy-2.3.2-cp313-cp313-win32.whl", hash = "sha256:906a30249315f9c8e17b085cc5f87d3f369b35fedd0051d4a84686967bdbbd0b", size = 6311843, upload-time = "2025-07-24T20:49:24.444Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6f/a428fd1cb7ed39b4280d057720fed5121b0d7754fd2a9768640160f5517b/numpy-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:c63d95dc9d67b676e9108fe0d2182987ccb0f11933c1e8959f42fa0da8d4fa56", size = 12782876, upload-time = "2025-07-24T20:49:43.227Z" }, - { url = "https://files.pythonhosted.org/packages/65/85/4ea455c9040a12595fb6c43f2c217257c7b52dd0ba332c6a6c1d28b289fe/numpy-2.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:b05a89f2fb84d21235f93de47129dd4f11c16f64c87c33f5e284e6a3a54e43f2", size = 10192786, upload-time = "2025-07-24T20:49:59.443Z" }, - { url = "https://files.pythonhosted.org/packages/80/23/8278f40282d10c3f258ec3ff1b103d4994bcad78b0cba9208317f6bb73da/numpy-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e6ecfeddfa83b02318f4d84acf15fbdbf9ded18e46989a15a8b6995dfbf85ab", size = 21047395, upload-time = "2025-07-24T20:45:58.821Z" }, - { url = "https://files.pythonhosted.org/packages/1f/2d/624f2ce4a5df52628b4ccd16a4f9437b37c35f4f8a50d00e962aae6efd7a/numpy-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:508b0eada3eded10a3b55725b40806a4b855961040180028f52580c4729916a2", size = 14300374, upload-time = "2025-07-24T20:46:20.207Z" }, - { url = "https://files.pythonhosted.org/packages/f6/62/ff1e512cdbb829b80a6bd08318a58698867bca0ca2499d101b4af063ee97/numpy-2.3.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:754d6755d9a7588bdc6ac47dc4ee97867271b17cee39cb87aef079574366db0a", size = 5228864, upload-time = "2025-07-24T20:46:30.58Z" }, - { url = "https://files.pythonhosted.org/packages/7d/8e/74bc18078fff03192d4032cfa99d5a5ca937807136d6f5790ce07ca53515/numpy-2.3.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f66e7d2b2d7712410d3bc5684149040ef5f19856f20277cd17ea83e5006286", size = 6737533, upload-time = "2025-07-24T20:46:46.111Z" }, - { url = "https://files.pythonhosted.org/packages/19/ea/0731efe2c9073ccca5698ef6a8c3667c4cf4eea53fcdcd0b50140aba03bc/numpy-2.3.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6ea4e5a65d5a90c7d286ddff2b87f3f4ad61faa3db8dabe936b34c2275b6f8", size = 14352007, upload-time = "2025-07-24T20:47:07.1Z" }, - { url = "https://files.pythonhosted.org/packages/cf/90/36be0865f16dfed20f4bc7f75235b963d5939707d4b591f086777412ff7b/numpy-2.3.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3ef07ec8cbc8fc9e369c8dcd52019510c12da4de81367d8b20bc692aa07573a", size = 16701914, upload-time = "2025-07-24T20:47:32.459Z" }, - { url = "https://files.pythonhosted.org/packages/94/30/06cd055e24cb6c38e5989a9e747042b4e723535758e6153f11afea88c01b/numpy-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:27c9f90e7481275c7800dc9c24b7cc40ace3fdb970ae4d21eaff983a32f70c91", size = 16132708, upload-time = "2025-07-24T20:47:58.129Z" }, - { url = "https://files.pythonhosted.org/packages/9a/14/ecede608ea73e58267fd7cb78f42341b3b37ba576e778a1a06baffbe585c/numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5", size = 18651678, upload-time = "2025-07-24T20:48:25.402Z" }, - { url = "https://files.pythonhosted.org/packages/40/f3/2fe6066b8d07c3685509bc24d56386534c008b462a488b7f503ba82b8923/numpy-2.3.2-cp313-cp313t-win32.whl", hash = "sha256:c771cfac34a4f2c0de8e8c97312d07d64fd8f8ed45bc9f5726a7e947270152b5", size = 6441832, upload-time = "2025-07-24T20:48:37.181Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ba/0937d66d05204d8f28630c9c60bc3eda68824abde4cf756c4d6aad03b0c6/numpy-2.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:72dbebb2dcc8305c431b2836bcc66af967df91be793d63a24e3d9b741374c450", size = 12927049, upload-time = "2025-07-24T20:48:56.24Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ed/13542dd59c104d5e654dfa2ac282c199ba64846a74c2c4bcdbc3a0f75df1/numpy-2.3.2-cp313-cp313t-win_arm64.whl", hash = "sha256:72c6df2267e926a6d5286b0a6d556ebe49eae261062059317837fda12ddf0c1a", size = 10262935, upload-time = "2025-07-24T20:49:13.136Z" }, - { url = "https://files.pythonhosted.org/packages/c9/7c/7659048aaf498f7611b783e000c7268fcc4dcf0ce21cd10aad7b2e8f9591/numpy-2.3.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:448a66d052d0cf14ce9865d159bfc403282c9bc7bb2a31b03cc18b651eca8b1a", size = 20950906, upload-time = "2025-07-24T20:50:30.346Z" }, - { url = "https://files.pythonhosted.org/packages/80/db/984bea9d4ddf7112a04cfdfb22b1050af5757864cfffe8e09e44b7f11a10/numpy-2.3.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:546aaf78e81b4081b2eba1d105c3b34064783027a06b3ab20b6eba21fb64132b", size = 14185607, upload-time = "2025-07-24T20:50:51.923Z" }, - { url = "https://files.pythonhosted.org/packages/e4/76/b3d6f414f4eca568f469ac112a3b510938d892bc5a6c190cb883af080b77/numpy-2.3.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:87c930d52f45df092f7578889711a0768094debf73cfcde105e2d66954358125", size = 5114110, upload-time = "2025-07-24T20:51:01.041Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d2/6f5e6826abd6bca52392ed88fe44a4b52aacb60567ac3bc86c67834c3a56/numpy-2.3.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:8dc082ea901a62edb8f59713c6a7e28a85daddcb67454c839de57656478f5b19", size = 6642050, upload-time = "2025-07-24T20:51:11.64Z" }, - { url = "https://files.pythonhosted.org/packages/c4/43/f12b2ade99199e39c73ad182f103f9d9791f48d885c600c8e05927865baf/numpy-2.3.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af58de8745f7fa9ca1c0c7c943616c6fe28e75d0c81f5c295810e3c83b5be92f", size = 14296292, upload-time = "2025-07-24T20:51:33.488Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f9/77c07d94bf110a916b17210fac38680ed8734c236bfed9982fd8524a7b47/numpy-2.3.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed5527c4cf10f16c6d0b6bee1f89958bccb0ad2522c8cadc2efd318bcd545f5", size = 16638913, upload-time = "2025-07-24T20:51:58.517Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d1/9d9f2c8ea399cc05cfff8a7437453bd4e7d894373a93cdc46361bbb49a7d/numpy-2.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:095737ed986e00393ec18ec0b21b47c22889ae4b0cd2d5e88342e08b01141f58", size = 16071180, upload-time = "2025-07-24T20:52:22.827Z" }, - { url = "https://files.pythonhosted.org/packages/4c/41/82e2c68aff2a0c9bf315e47d61951099fed65d8cb2c8d9dc388cb87e947e/numpy-2.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5e40e80299607f597e1a8a247ff8d71d79c5b52baa11cc1cce30aa92d2da6e0", size = 18576809, upload-time = "2025-07-24T20:52:51.015Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/4b4fd3efb0837ed252d0f583c5c35a75121038a8c4e065f2c259be06d2d8/numpy-2.3.2-cp314-cp314-win32.whl", hash = "sha256:7d6e390423cc1f76e1b8108c9b6889d20a7a1f59d9a60cac4a050fa734d6c1e2", size = 6366410, upload-time = "2025-07-24T20:56:44.949Z" }, - { url = "https://files.pythonhosted.org/packages/11/9e/b4c24a6b8467b61aced5c8dc7dcfce23621baa2e17f661edb2444a418040/numpy-2.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:b9d0878b21e3918d76d2209c924ebb272340da1fb51abc00f986c258cd5e957b", size = 12918821, upload-time = "2025-07-24T20:57:06.479Z" }, - { url = "https://files.pythonhosted.org/packages/0e/0f/0dc44007c70b1007c1cef86b06986a3812dd7106d8f946c09cfa75782556/numpy-2.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:2738534837c6a1d0c39340a190177d7d66fdf432894f469728da901f8f6dc910", size = 10477303, upload-time = "2025-07-24T20:57:22.879Z" }, - { url = "https://files.pythonhosted.org/packages/8b/3e/075752b79140b78ddfc9c0a1634d234cfdbc6f9bbbfa6b7504e445ad7d19/numpy-2.3.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4d002ecf7c9b53240be3bb69d80f86ddbd34078bae04d87be81c1f58466f264e", size = 21047524, upload-time = "2025-07-24T20:53:22.086Z" }, - { url = "https://files.pythonhosted.org/packages/fe/6d/60e8247564a72426570d0e0ea1151b95ce5bd2f1597bb878a18d32aec855/numpy-2.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:293b2192c6bcce487dbc6326de5853787f870aeb6c43f8f9c6496db5b1781e45", size = 14300519, upload-time = "2025-07-24T20:53:44.053Z" }, - { url = "https://files.pythonhosted.org/packages/4d/73/d8326c442cd428d47a067070c3ac6cc3b651a6e53613a1668342a12d4479/numpy-2.3.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0a4f2021a6da53a0d580d6ef5db29947025ae8b35b3250141805ea9a32bbe86b", size = 5228972, upload-time = "2025-07-24T20:53:53.81Z" }, - { url = "https://files.pythonhosted.org/packages/34/2e/e71b2d6dad075271e7079db776196829019b90ce3ece5c69639e4f6fdc44/numpy-2.3.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9c144440db4bf3bb6372d2c3e49834cc0ff7bb4c24975ab33e01199e645416f2", size = 6737439, upload-time = "2025-07-24T20:54:04.742Z" }, - { url = "https://files.pythonhosted.org/packages/15/b0/d004bcd56c2c5e0500ffc65385eb6d569ffd3363cb5e593ae742749b2daa/numpy-2.3.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f92d6c2a8535dc4fe4419562294ff957f83a16ebdec66df0805e473ffaad8bd0", size = 14352479, upload-time = "2025-07-24T20:54:25.819Z" }, - { url = "https://files.pythonhosted.org/packages/11/e3/285142fcff8721e0c99b51686426165059874c150ea9ab898e12a492e291/numpy-2.3.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cefc2219baa48e468e3db7e706305fcd0c095534a192a08f31e98d83a7d45fb0", size = 16702805, upload-time = "2025-07-24T20:54:50.814Z" }, - { url = "https://files.pythonhosted.org/packages/33/c3/33b56b0e47e604af2c7cd065edca892d180f5899599b76830652875249a3/numpy-2.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76c3e9501ceb50b2ff3824c3589d5d1ab4ac857b0ee3f8f49629d0de55ecf7c2", size = 16133830, upload-time = "2025-07-24T20:55:17.306Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ae/7b1476a1f4d6a48bc669b8deb09939c56dd2a439db1ab03017844374fb67/numpy-2.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:122bf5ed9a0221b3419672493878ba4967121514b1d7d4656a7580cd11dddcbf", size = 18652665, upload-time = "2025-07-24T20:55:46.665Z" }, - { url = "https://files.pythonhosted.org/packages/14/ba/5b5c9978c4bb161034148ade2de9db44ec316fab89ce8c400db0e0c81f86/numpy-2.3.2-cp314-cp314t-win32.whl", hash = "sha256:6f1ae3dcb840edccc45af496f312528c15b1f79ac318169d094e85e4bb35fdf1", size = 6514777, upload-time = "2025-07-24T20:55:57.66Z" }, - { url = "https://files.pythonhosted.org/packages/eb/46/3dbaf0ae7c17cdc46b9f662c56da2054887b8d9e737c1476f335c83d33db/numpy-2.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b", size = 13111856, upload-time = "2025-07-24T20:56:17.318Z" }, - { url = "https://files.pythonhosted.org/packages/c1/9e/1652778bce745a67b5fe05adde60ed362d38eb17d919a540e813d30f6874/numpy-2.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:092aeb3449833ea9c0bf0089d70c29ae480685dd2377ec9cdbbb620257f84631", size = 10544226, upload-time = "2025-07-24T20:56:34.509Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ea/50ebc91d28b275b23b7128ef25c3d08152bc4068f42742867e07a870a42a/numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15", size = 21130338, upload-time = "2025-07-24T20:57:54.37Z" }, - { url = "https://files.pythonhosted.org/packages/9f/57/cdd5eac00dd5f137277355c318a955c0d8fb8aa486020c22afd305f8b88f/numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec", size = 14375776, upload-time = "2025-07-24T20:58:16.303Z" }, - { url = "https://files.pythonhosted.org/packages/83/85/27280c7f34fcd305c2209c0cdca4d70775e4859a9eaa92f850087f8dea50/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712", size = 5304882, upload-time = "2025-07-24T20:58:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/48/b4/6500b24d278e15dd796f43824e69939d00981d37d9779e32499e823aa0aa/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c", size = 6818405, upload-time = "2025-07-24T20:58:37.341Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c9/142c1e03f199d202da8e980c2496213509291b6024fd2735ad28ae7065c7/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296", size = 14419651, upload-time = "2025-07-24T20:58:59.048Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/8023e87cbea31a750a6c00ff9427d65ebc5fef104a136bfa69f76266d614/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981", size = 16760166, upload-time = "2025-07-24T21:28:56.38Z" }, - { url = "https://files.pythonhosted.org/packages/78/e3/6690b3f85a05506733c7e90b577e4762517404ea78bab2ca3a5cb1aeb78d/numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619", size = 12977811, upload-time = "2025-07-24T21:29:18.234Z" }, + { url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d", size = 21259253, upload-time = "2025-09-09T15:56:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569", size = 14450980, upload-time = "2025-09-09T15:56:05.926Z" }, + { url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f", size = 5379709, upload-time = "2025-09-09T15:56:07.95Z" }, + { url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125", size = 6913923, upload-time = "2025-09-09T15:56:09.443Z" }, + { url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48", size = 14589591, upload-time = "2025-09-09T15:56:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6", size = 16938714, upload-time = "2025-09-09T15:56:14.637Z" }, + { url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa", size = 16370592, upload-time = "2025-09-09T15:56:17.285Z" }, + { url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30", size = 18884474, upload-time = "2025-09-09T15:56:20.943Z" }, + { url = "https://files.pythonhosted.org/packages/45/fa/7f43ba10c77575e8be7b0138d107e4f44ca4a1ef322cd16980ea3e8b8222/numpy-2.3.3-cp311-cp311-win32.whl", hash = "sha256:eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57", size = 6599794, upload-time = "2025-09-09T15:56:23.258Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a2/a4f78cb2241fe5664a22a10332f2be886dcdea8784c9f6a01c272da9b426/numpy-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa", size = 13088104, upload-time = "2025-09-09T15:56:25.476Z" }, + { url = "https://files.pythonhosted.org/packages/79/64/e424e975adbd38282ebcd4891661965b78783de893b381cbc4832fb9beb2/numpy-2.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7", size = 10460772, upload-time = "2025-09-09T15:56:27.679Z" }, + { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, + { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, + { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, + { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, + { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, + { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" }, + { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, + { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, + { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" }, + { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" }, + { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, + { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, + { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, + { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, + { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, + { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" }, + { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, + { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, + { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, + { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" }, + { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" }, + { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" }, + { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, + { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, + { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, + { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" }, + { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" }, + { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e", size = 21131019, upload-time = "2025-09-09T15:58:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150", size = 14376288, upload-time = "2025-09-09T15:58:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3", size = 5305425, upload-time = "2025-09-09T15:58:48.6Z" }, + { url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0", size = 6819053, upload-time = "2025-09-09T15:58:50.401Z" }, + { url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e", size = 14420354, upload-time = "2025-09-09T15:58:52.704Z" }, + { url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db", size = 16760413, upload-time = "2025-09-09T15:58:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844, upload-time = "2025-09-09T15:58:57.359Z" }, ] [[package]] @@ -752,7 +796,7 @@ dev = [ [[package]] name = "overture-schema-foundation" -source = { editable = "packages/overture-schema-foundation" } +source = { editable = "packages/overture-schema-system" } dependencies = [ { name = "pydantic" }, { name = "shapely" }, @@ -930,7 +974,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.11.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -938,9 +982,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, ] [package.optional-dependencies] @@ -1058,7 +1102,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.1" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1069,23 +1113,23 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] name = "pytest-cov" -version = "6.2.1" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, ] [[package]] @@ -1103,123 +1147,160 @@ wheels = [ [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] name = "ruff" -version = "0.12.4" +version = "0.13.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/8e/f9f9ca747fea8e3ac954e3690d4698c9737c23b51731d02df999c150b1c9/ruff-0.13.3.tar.gz", hash = "sha256:5b0ba0db740eefdfbcce4299f49e9eaefc643d4d007749d77d047c2bab19908e", size = 5438533, upload-time = "2025-10-02T19:29:31.582Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" }, - { url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" }, - { url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" }, - { url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" }, - { url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" }, - { url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" }, - { url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" }, - { url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" }, - { url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" }, - { url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" }, - { url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" }, - { url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" }, - { url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" }, - { url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" }, + { url = "https://files.pythonhosted.org/packages/d2/33/8f7163553481466a92656d35dea9331095122bb84cf98210bef597dd2ecd/ruff-0.13.3-py3-none-linux_armv6l.whl", hash = "sha256:311860a4c5e19189c89d035638f500c1e191d283d0cc2f1600c8c80d6dcd430c", size = 12484040, upload-time = "2025-10-02T19:28:49.199Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b5/4a21a4922e5dd6845e91896b0d9ef493574cbe061ef7d00a73c61db531af/ruff-0.13.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2bdad6512fb666b40fcadb65e33add2b040fc18a24997d2e47fee7d66f7fcae2", size = 13122975, upload-time = "2025-10-02T19:28:52.446Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/15649af836d88c9f154e5be87e64ae7d2b1baa5a3ef317cb0c8fafcd882d/ruff-0.13.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fc6fa4637284708d6ed4e5e970d52fc3b76a557d7b4e85a53013d9d201d93286", size = 12346621, upload-time = "2025-10-02T19:28:54.712Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/bcbccb8141305f9a6d3f72549dd82d1134299177cc7eaf832599700f95a7/ruff-0.13.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c9e6469864f94a98f412f20ea143d547e4c652f45e44f369d7b74ee78185838", size = 12574408, upload-time = "2025-10-02T19:28:56.679Z" }, + { url = "https://files.pythonhosted.org/packages/ce/19/0f3681c941cdcfa2d110ce4515624c07a964dc315d3100d889fcad3bfc9e/ruff-0.13.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5bf62b705f319476c78891e0e97e965b21db468b3c999086de8ffb0d40fd2822", size = 12285330, upload-time = "2025-10-02T19:28:58.79Z" }, + { url = "https://files.pythonhosted.org/packages/10/f8/387976bf00d126b907bbd7725219257feea58650e6b055b29b224d8cb731/ruff-0.13.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cc1abed87ce40cb07ee0667ce99dbc766c9f519eabfd948ed87295d8737c60", size = 13980815, upload-time = "2025-10-02T19:29:01.577Z" }, + { url = "https://files.pythonhosted.org/packages/0c/a6/7c8ec09d62d5a406e2b17d159e4817b63c945a8b9188a771193b7e1cc0b5/ruff-0.13.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4fb75e7c402d504f7a9a259e0442b96403fa4a7310ffe3588d11d7e170d2b1e3", size = 14987733, upload-time = "2025-10-02T19:29:04.036Z" }, + { url = "https://files.pythonhosted.org/packages/97/e5/f403a60a12258e0fd0c2195341cfa170726f254c788673495d86ab5a9a9d/ruff-0.13.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b951f9d9afb39330b2bdd2dd144ce1c1335881c277837ac1b50bfd99985ed3", size = 14439848, upload-time = "2025-10-02T19:29:06.684Z" }, + { url = "https://files.pythonhosted.org/packages/39/49/3de381343e89364c2334c9f3268b0349dc734fc18b2d99a302d0935c8345/ruff-0.13.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6052f8088728898e0a449f0dde8fafc7ed47e4d878168b211977e3e7e854f662", size = 13421890, upload-time = "2025-10-02T19:29:08.767Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b5/c0feca27d45ae74185a6bacc399f5d8920ab82df2d732a17213fb86a2c4c/ruff-0.13.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc742c50f4ba72ce2a3be362bd359aef7d0d302bf7637a6f942eaa763bd292af", size = 13444870, upload-time = "2025-10-02T19:29:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/50/a1/b655298a1f3fda4fdc7340c3f671a4b260b009068fbeb3e4e151e9e3e1bf/ruff-0.13.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:8e5640349493b378431637019366bbd73c927e515c9c1babfea3e932f5e68e1d", size = 13691599, upload-time = "2025-10-02T19:29:13.353Z" }, + { url = "https://files.pythonhosted.org/packages/32/b0/a8705065b2dafae007bcae21354e6e2e832e03eb077bb6c8e523c2becb92/ruff-0.13.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b139f638a80eae7073c691a5dd8d581e0ba319540be97c343d60fb12949c8d0", size = 12421893, upload-time = "2025-10-02T19:29:15.668Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1e/cbe7082588d025cddbb2f23e6dfef08b1a2ef6d6f8328584ad3015b5cebd/ruff-0.13.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6b547def0a40054825de7cfa341039ebdfa51f3d4bfa6a0772940ed351d2746c", size = 12267220, upload-time = "2025-10-02T19:29:17.583Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/4086f9c43f85e0755996d09bdcb334b6fee9b1eabdf34e7d8b877fadf964/ruff-0.13.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9cc48a3564423915c93573f1981d57d101e617839bef38504f85f3677b3a0a3e", size = 13177818, upload-time = "2025-10-02T19:29:19.943Z" }, + { url = "https://files.pythonhosted.org/packages/9b/de/7b5db7e39947d9dc1c5f9f17b838ad6e680527d45288eeb568e860467010/ruff-0.13.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1a993b17ec03719c502881cb2d5f91771e8742f2ca6de740034433a97c561989", size = 13618715, upload-time = "2025-10-02T19:29:22.527Z" }, + { url = "https://files.pythonhosted.org/packages/28/d3/bb25ee567ce2f61ac52430cf99f446b0e6d49bdfa4188699ad005fdd16aa/ruff-0.13.3-py3-none-win32.whl", hash = "sha256:f14e0d1fe6460f07814d03c6e32e815bff411505178a1f539a38f6097d3e8ee3", size = 12334488, upload-time = "2025-10-02T19:29:24.782Z" }, + { url = "https://files.pythonhosted.org/packages/cf/49/12f5955818a1139eed288753479ba9d996f6ea0b101784bb1fe6977ec128/ruff-0.13.3-py3-none-win_amd64.whl", hash = "sha256:621e2e5812b691d4f244638d693e640f188bacbb9bc793ddd46837cea0503dd2", size = 13455262, upload-time = "2025-10-02T19:29:26.882Z" }, + { url = "https://files.pythonhosted.org/packages/fe/72/7b83242b26627a00e3af70d0394d68f8f02750d642567af12983031777fc/ruff-0.13.3-py3-none-win_arm64.whl", hash = "sha256:9e9e9d699841eaf4c2c798fa783df2fabc680b72059a02ca0ed81c460bc58330", size = 12538484, upload-time = "2025-10-02T19:29:28.951Z" }, ] [[package]] name = "shapely" -version = "2.1.1" +version = "2.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489, upload-time = "2025-09-24T13:51:41.432Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/fa/f18025c95b86116dd8f1ec58cab078bd59ab51456b448136ca27463be533/shapely-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8ccc872a632acb7bdcb69e5e78df27213f7efd195882668ffba5405497337c6", size = 1825117, upload-time = "2025-05-19T11:03:43.547Z" }, - { url = "https://files.pythonhosted.org/packages/c7/65/46b519555ee9fb851234288be7c78be11e6260995281071d13abf2c313d0/shapely-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f24f2ecda1e6c091da64bcbef8dd121380948074875bd1b247b3d17e99407099", size = 1628541, upload-time = "2025-05-19T11:03:45.162Z" }, - { url = "https://files.pythonhosted.org/packages/29/51/0b158a261df94e33505eadfe737db9531f346dfa60850945ad25fd4162f1/shapely-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45112a5be0b745b49e50f8829ce490eb67fefb0cea8d4f8ac5764bfedaa83d2d", size = 2948453, upload-time = "2025-05-19T11:03:46.681Z" }, - { url = "https://files.pythonhosted.org/packages/a9/4f/6c9bb4bd7b1a14d7051641b9b479ad2a643d5cbc382bcf5bd52fd0896974/shapely-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c10ce6f11904d65e9bbb3e41e774903c944e20b3f0b282559885302f52f224a", size = 3057029, upload-time = "2025-05-19T11:03:48.346Z" }, - { url = "https://files.pythonhosted.org/packages/89/0b/ad1b0af491d753a83ea93138eee12a4597f763ae12727968d05934fe7c78/shapely-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:61168010dfe4e45f956ffbbaf080c88afce199ea81eb1f0ac43230065df320bd", size = 3894342, upload-time = "2025-05-19T11:03:49.602Z" }, - { url = "https://files.pythonhosted.org/packages/7d/96/73232c5de0b9fdf0ec7ddfc95c43aaf928740e87d9f168bff0e928d78c6d/shapely-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cacf067cdff741cd5c56a21c52f54ece4e4dad9d311130493a791997da4a886b", size = 4056766, upload-time = "2025-05-19T11:03:51.252Z" }, - { url = "https://files.pythonhosted.org/packages/43/cc/eec3c01f754f5b3e0c47574b198f9deb70465579ad0dad0e1cef2ce9e103/shapely-2.1.1-cp310-cp310-win32.whl", hash = "sha256:23b8772c3b815e7790fb2eab75a0b3951f435bc0fce7bb146cb064f17d35ab4f", size = 1523744, upload-time = "2025-05-19T11:03:52.624Z" }, - { url = "https://files.pythonhosted.org/packages/50/fc/a7187e6dadb10b91e66a9e715d28105cde6489e1017cce476876185a43da/shapely-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:2c7b2b6143abf4fa77851cef8ef690e03feade9a0d48acd6dc41d9e0e78d7ca6", size = 1703061, upload-time = "2025-05-19T11:03:54.695Z" }, - { url = "https://files.pythonhosted.org/packages/19/97/2df985b1e03f90c503796ad5ecd3d9ed305123b64d4ccb54616b30295b29/shapely-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587a1aa72bc858fab9b8c20427b5f6027b7cbc92743b8e2c73b9de55aa71c7a7", size = 1819368, upload-time = "2025-05-19T11:03:55.937Z" }, - { url = "https://files.pythonhosted.org/packages/56/17/504518860370f0a28908b18864f43d72f03581e2b6680540ca668f07aa42/shapely-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fa5c53b0791a4b998f9ad84aad456c988600757a96b0a05e14bba10cebaaaea", size = 1625362, upload-time = "2025-05-19T11:03:57.06Z" }, - { url = "https://files.pythonhosted.org/packages/36/a1/9677337d729b79fce1ef3296aac6b8ef4743419086f669e8a8070eff8f40/shapely-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aabecd038841ab5310d23495253f01c2a82a3aedae5ab9ca489be214aa458aa7", size = 2999005, upload-time = "2025-05-19T11:03:58.692Z" }, - { url = "https://files.pythonhosted.org/packages/a2/17/e09357274699c6e012bbb5a8ea14765a4d5860bb658df1931c9f90d53bd3/shapely-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586f6aee1edec04e16227517a866df3e9a2e43c1f635efc32978bb3dc9c63753", size = 3108489, upload-time = "2025-05-19T11:04:00.059Z" }, - { url = "https://files.pythonhosted.org/packages/17/5d/93a6c37c4b4e9955ad40834f42b17260ca74ecf36df2e81bb14d12221b90/shapely-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9878b9e37ad26c72aada8de0c9cfe418d9e2ff36992a1693b7f65a075b28647", size = 3945727, upload-time = "2025-05-19T11:04:01.786Z" }, - { url = "https://files.pythonhosted.org/packages/a3/1a/ad696648f16fd82dd6bfcca0b3b8fbafa7aacc13431c7fc4c9b49e481681/shapely-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9a531c48f289ba355e37b134e98e28c557ff13965d4653a5228d0f42a09aed0", size = 4109311, upload-time = "2025-05-19T11:04:03.134Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/150dd245beab179ec0d4472bf6799bf18f21b1efbef59ac87de3377dbf1c/shapely-2.1.1-cp311-cp311-win32.whl", hash = "sha256:4866de2673a971820c75c0167b1f1cd8fb76f2d641101c23d3ca021ad0449bab", size = 1522982, upload-time = "2025-05-19T11:04:05.217Z" }, - { url = "https://files.pythonhosted.org/packages/93/5b/842022c00fbb051083c1c85430f3bb55565b7fd2d775f4f398c0ba8052ce/shapely-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:20a9d79958b3d6c70d8a886b250047ea32ff40489d7abb47d01498c704557a93", size = 1703872, upload-time = "2025-05-19T11:04:06.791Z" }, - { url = "https://files.pythonhosted.org/packages/fb/64/9544dc07dfe80a2d489060791300827c941c451e2910f7364b19607ea352/shapely-2.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2827365b58bf98efb60affc94a8e01c56dd1995a80aabe4b701465d86dcbba43", size = 1833021, upload-time = "2025-05-19T11:04:08.022Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/fb5f545e72e89b6a0f04a0effda144f5be956c9c312c7d4e00dfddbddbcf/shapely-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c551f7fa7f1e917af2347fe983f21f212863f1d04f08eece01e9c275903fad", size = 1643018, upload-time = "2025-05-19T11:04:09.343Z" }, - { url = "https://files.pythonhosted.org/packages/03/46/61e03edba81de729f09d880ce7ae5c1af873a0814206bbfb4402ab5c3388/shapely-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78dec4d4fbe7b1db8dc36de3031767e7ece5911fb7782bc9e95c5cdec58fb1e9", size = 2986417, upload-time = "2025-05-19T11:04:10.56Z" }, - { url = "https://files.pythonhosted.org/packages/1f/1e/83ec268ab8254a446b4178b45616ab5822d7b9d2b7eb6e27cf0b82f45601/shapely-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:872d3c0a7b8b37da0e23d80496ec5973c4692920b90de9f502b5beb994bbaaef", size = 3098224, upload-time = "2025-05-19T11:04:11.903Z" }, - { url = "https://files.pythonhosted.org/packages/f1/44/0c21e7717c243e067c9ef8fa9126de24239f8345a5bba9280f7bb9935959/shapely-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2e2b9125ebfbc28ecf5353511de62f75a8515ae9470521c9a693e4bb9fbe0cf1", size = 3925982, upload-time = "2025-05-19T11:04:13.224Z" }, - { url = "https://files.pythonhosted.org/packages/15/50/d3b4e15fefc103a0eb13d83bad5f65cd6e07a5d8b2ae920e767932a247d1/shapely-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4b96cea171b3d7f6786976a0520f178c42792897653ecca0c5422fb1e6946e6d", size = 4089122, upload-time = "2025-05-19T11:04:14.477Z" }, - { url = "https://files.pythonhosted.org/packages/bd/05/9a68f27fc6110baeedeeebc14fd86e73fa38738c5b741302408fb6355577/shapely-2.1.1-cp312-cp312-win32.whl", hash = "sha256:39dca52201e02996df02e447f729da97cfb6ff41a03cb50f5547f19d02905af8", size = 1522437, upload-time = "2025-05-19T11:04:16.203Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e9/a4560e12b9338842a1f82c9016d2543eaa084fce30a1ca11991143086b57/shapely-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:13d643256f81d55a50013eff6321142781cf777eb6a9e207c2c9e6315ba6044a", size = 1703479, upload-time = "2025-05-19T11:04:18.497Z" }, - { url = "https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3004a644d9e89e26c20286d5fdc10f41b1744c48ce910bd1867fdff963fe6c48", size = 1832107, upload-time = "2025-05-19T11:04:19.736Z" }, - { url = "https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1415146fa12d80a47d13cfad5310b3c8b9c2aa8c14a0c845c9d3d75e77cb54f6", size = 1642355, upload-time = "2025-05-19T11:04:21.035Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/6d28b43d53fea56de69c744e34c2b999ed4042f7a811dc1bceb876071c95/shapely-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21fcab88b7520820ec16d09d6bea68652ca13993c84dffc6129dc3607c95594c", size = 2968871, upload-time = "2025-05-19T11:04:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ce6a5cc52c974b291237a96c08c5592e50f066871704fb5b12be2639d9026a", size = 3080830, upload-time = "2025-05-19T11:04:23.997Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fe/f4a03d81abd96a6ce31c49cd8aaba970eaaa98e191bd1e4d43041e57ae5a/shapely-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:04e4c12a45a1d70aeb266618d8cf81a2de9c4df511b63e105b90bfdfb52146de", size = 3908961, upload-time = "2025-05-19T11:04:25.702Z" }, - { url = "https://files.pythonhosted.org/packages/ef/59/7605289a95a6844056a2017ab36d9b0cb9d6a3c3b5317c1f968c193031c9/shapely-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ca74d851ca5264aae16c2b47e96735579686cb69fa93c4078070a0ec845b8d8", size = 4079623, upload-time = "2025-05-19T11:04:27.171Z" }, - { url = "https://files.pythonhosted.org/packages/bc/4d/9fea036eff2ef4059d30247128b2d67aaa5f0b25e9fc27e1d15cc1b84704/shapely-2.1.1-cp313-cp313-win32.whl", hash = "sha256:fd9130501bf42ffb7e0695b9ea17a27ae8ce68d50b56b6941c7f9b3d3453bc52", size = 1521916, upload-time = "2025-05-19T11:04:28.405Z" }, - { url = "https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:ab8d878687b438a2f4c138ed1a80941c6ab0029e0f4c785ecfe114413b498a97", size = 1702746, upload-time = "2025-05-19T11:04:29.643Z" }, - { url = "https://files.pythonhosted.org/packages/60/36/b1452e3e7f35f5f6454d96f3be6e2bb87082720ff6c9437ecc215fa79be0/shapely-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c062384316a47f776305ed2fa22182717508ffdeb4a56d0ff4087a77b2a0f6d", size = 1833482, upload-time = "2025-05-19T11:04:30.852Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ca/8e6f59be0718893eb3e478141285796a923636dc8f086f83e5b0ec0036d0/shapely-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4ecf6c196b896e8f1360cc219ed4eee1c1e5f5883e505d449f263bd053fb8c05", size = 1642256, upload-time = "2025-05-19T11:04:32.068Z" }, - { url = "https://files.pythonhosted.org/packages/ab/78/0053aea449bb1d4503999525fec6232f049abcdc8df60d290416110de943/shapely-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb00070b4c4860f6743c600285109c273cca5241e970ad56bb87bef0be1ea3a0", size = 3016614, upload-time = "2025-05-19T11:04:33.7Z" }, - { url = "https://files.pythonhosted.org/packages/ee/53/36f1b1de1dfafd1b457dcbafa785b298ce1b8a3e7026b79619e708a245d5/shapely-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14a9afa5fa980fbe7bf63706fdfb8ff588f638f145a1d9dbc18374b5b7de913", size = 3093542, upload-time = "2025-05-19T11:04:34.952Z" }, - { url = "https://files.pythonhosted.org/packages/b9/bf/0619f37ceec6b924d84427c88835b61f27f43560239936ff88915c37da19/shapely-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b640e390dabde790e3fb947198b466e63223e0a9ccd787da5f07bcb14756c28d", size = 3945961, upload-time = "2025-05-19T11:04:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/93/c9/20ca4afeb572763b07a7997f00854cb9499df6af85929e93012b189d8917/shapely-2.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:69e08bf9697c1b73ec6aa70437db922bafcea7baca131c90c26d59491a9760f9", size = 4089514, upload-time = "2025-05-19T11:04:37.683Z" }, - { url = "https://files.pythonhosted.org/packages/33/6a/27036a5a560b80012a544366bceafd491e8abb94a8db14047b5346b5a749/shapely-2.1.1-cp313-cp313t-win32.whl", hash = "sha256:ef2d09d5a964cc90c2c18b03566cf918a61c248596998a0301d5b632beadb9db", size = 1540607, upload-time = "2025-05-19T11:04:38.925Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f1/5e9b3ba5c7aa7ebfaf269657e728067d16a7c99401c7973ddf5f0cf121bd/shapely-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8cb8f17c377260452e9d7720eeaf59082c5f8ea48cf104524d953e5d36d4bdb7", size = 1723061, upload-time = "2025-05-19T11:04:40.082Z" }, + { url = "https://files.pythonhosted.org/packages/05/89/c3548aa9b9812a5d143986764dededfa48d817714e947398bdda87c77a72/shapely-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7ae48c236c0324b4e139bea88a306a04ca630f49be66741b340729d380d8f52f", size = 1825959, upload-time = "2025-09-24T13:50:00.682Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8a/7ebc947080442edd614ceebe0ce2cdbd00c25e832c240e1d1de61d0e6b38/shapely-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eba6710407f1daa8e7602c347dfc94adc02205ec27ed956346190d66579eb9ea", size = 1629196, upload-time = "2025-09-24T13:50:03.447Z" }, + { url = "https://files.pythonhosted.org/packages/c8/86/c9c27881c20d00fc409e7e059de569d5ed0abfcec9c49548b124ebddea51/shapely-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef4a456cc8b7b3d50ccec29642aa4aeda959e9da2fe9540a92754770d5f0cf1f", size = 2951065, upload-time = "2025-09-24T13:50:05.266Z" }, + { url = "https://files.pythonhosted.org/packages/50/8a/0ab1f7433a2a85d9e9aea5b1fbb333f3b09b309e7817309250b4b7b2cc7a/shapely-2.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e38a190442aacc67ff9f75ce60aec04893041f16f97d242209106d502486a142", size = 3058666, upload-time = "2025-09-24T13:50:06.872Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c6/5a30ffac9c4f3ffd5b7113a7f5299ccec4713acd5ee44039778a7698224e/shapely-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:40d784101f5d06a1fd30b55fc11ea58a61be23f930d934d86f19a180909908a4", size = 3966905, upload-time = "2025-09-24T13:50:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/9c/72/e92f3035ba43e53959007f928315a68fbcf2eeb4e5ededb6f0dc7ff1ecc3/shapely-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f6f6cd5819c50d9bcf921882784586aab34a4bd53e7553e175dece6db513a6f0", size = 4129260, upload-time = "2025-09-24T13:50:11.183Z" }, + { url = "https://files.pythonhosted.org/packages/42/24/605901b73a3d9f65fa958e63c9211f4be23d584da8a1a7487382fac7fdc5/shapely-2.1.2-cp310-cp310-win32.whl", hash = "sha256:fe9627c39c59e553c90f5bc3128252cb85dc3b3be8189710666d2f8bc3a5503e", size = 1544301, upload-time = "2025-09-24T13:50:12.521Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/6db795b8dd3919851856bd2ddd13ce434a748072f6fdee42ff30cbd3afa3/shapely-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:1d0bfb4b8f661b3b4ec3565fa36c340bfb1cda82087199711f86a88647d26b2f", size = 1722074, upload-time = "2025-09-24T13:50:13.909Z" }, + { url = "https://files.pythonhosted.org/packages/8f/8d/1ff672dea9ec6a7b5d422eb6d095ed886e2e523733329f75fdcb14ee1149/shapely-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91121757b0a36c9aac3427a651a7e6567110a4a67c97edf04f8d55d4765f6618", size = 1820038, upload-time = "2025-09-24T13:50:15.628Z" }, + { url = "https://files.pythonhosted.org/packages/4f/ce/28fab8c772ce5db23a0d86bf0adaee0c4c79d5ad1db766055fa3dab442e2/shapely-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16a9c722ba774cf50b5d4541242b4cce05aafd44a015290c82ba8a16931ff63d", size = 1626039, upload-time = "2025-09-24T13:50:16.881Z" }, + { url = "https://files.pythonhosted.org/packages/70/8b/868b7e3f4982f5006e9395c1e12343c66a8155c0374fdc07c0e6a1ab547d/shapely-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cc4f7397459b12c0b196c9efe1f9d7e92463cbba142632b4cc6d8bbbbd3e2b09", size = 3001519, upload-time = "2025-09-24T13:50:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/13/02/58b0b8d9c17c93ab6340edd8b7308c0c5a5b81f94ce65705819b7416dba5/shapely-2.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:136ab87b17e733e22f0961504d05e77e7be8c9b5a8184f685b4a91a84efe3c26", size = 3110842, upload-time = "2025-09-24T13:50:21.77Z" }, + { url = "https://files.pythonhosted.org/packages/af/61/8e389c97994d5f331dcffb25e2fa761aeedfb52b3ad9bcdd7b8671f4810a/shapely-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:16c5d0fc45d3aa0a69074979f4f1928ca2734fb2e0dde8af9611e134e46774e7", size = 4021316, upload-time = "2025-09-24T13:50:23.626Z" }, + { url = "https://files.pythonhosted.org/packages/d3/d4/9b2a9fe6039f9e42ccf2cb3e84f219fd8364b0c3b8e7bbc857b5fbe9c14c/shapely-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ddc759f72b5b2b0f54a7e7cde44acef680a55019eb52ac63a7af2cf17cb9cd2", size = 4178586, upload-time = "2025-09-24T13:50:25.443Z" }, + { url = "https://files.pythonhosted.org/packages/16/f6/9840f6963ed4decf76b08fd6d7fed14f8779fb7a62cb45c5617fa8ac6eab/shapely-2.1.2-cp311-cp311-win32.whl", hash = "sha256:2fa78b49485391224755a856ed3b3bd91c8455f6121fee0db0e71cefb07d0ef6", size = 1543961, upload-time = "2025-09-24T13:50:26.968Z" }, + { url = "https://files.pythonhosted.org/packages/38/1e/3f8ea46353c2a33c1669eb7327f9665103aa3a8dfe7f2e4ef714c210b2c2/shapely-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:c64d5c97b2f47e3cd9b712eaced3b061f2b71234b3fc263e0fcf7d889c6559dc", size = 1722856, upload-time = "2025-09-24T13:50:28.497Z" }, + { url = "https://files.pythonhosted.org/packages/24/c0/f3b6453cf2dfa99adc0ba6675f9aaff9e526d2224cbd7ff9c1a879238693/shapely-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe2533caae6a91a543dec62e8360fe86ffcdc42a7c55f9dfd0128a977a896b94", size = 1833550, upload-time = "2025-09-24T13:50:30.019Z" }, + { url = "https://files.pythonhosted.org/packages/86/07/59dee0bc4b913b7ab59ab1086225baca5b8f19865e6101db9ebb7243e132/shapely-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba4d1333cc0bc94381d6d4308d2e4e008e0bd128bdcff5573199742ee3634359", size = 1643556, upload-time = "2025-09-24T13:50:32.291Z" }, + { url = "https://files.pythonhosted.org/packages/26/29/a5397e75b435b9895cd53e165083faed5d12fd9626eadec15a83a2411f0f/shapely-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bd308103340030feef6c111d3eb98d50dc13feea33affc8a6f9fa549e9458a3", size = 2988308, upload-time = "2025-09-24T13:50:33.862Z" }, + { url = "https://files.pythonhosted.org/packages/b9/37/e781683abac55dde9771e086b790e554811a71ed0b2b8a1e789b7430dd44/shapely-2.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e7d4d7ad262a48bb44277ca12c7c78cb1b0f56b32c10734ec9a1d30c0b0c54b", size = 3099844, upload-time = "2025-09-24T13:50:35.459Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f3/9876b64d4a5a321b9dc482c92bb6f061f2fa42131cba643c699f39317cb9/shapely-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9eddfe513096a71896441a7c37db72da0687b34752c4e193577a145c71736fc", size = 3988842, upload-time = "2025-09-24T13:50:37.478Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/704c7292f7014c7e74ec84eddb7b109e1fbae74a16deae9c1504b1d15565/shapely-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:980c777c612514c0cf99bc8a9de6d286f5e186dcaf9091252fcd444e5638193d", size = 4152714, upload-time = "2025-09-24T13:50:39.9Z" }, + { url = "https://files.pythonhosted.org/packages/53/46/319c9dc788884ad0785242543cdffac0e6530e4d0deb6c4862bc4143dcf3/shapely-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9111274b88e4d7b54a95218e243282709b330ef52b7b86bc6aaf4f805306f454", size = 1542745, upload-time = "2025-09-24T13:50:41.414Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bf/cb6c1c505cb31e818e900b9312d514f381fbfa5c4363edfce0fcc4f8c1a4/shapely-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:743044b4cfb34f9a67205cee9279feaf60ba7d02e69febc2afc609047cb49179", size = 1722861, upload-time = "2025-09-24T13:50:43.35Z" }, + { url = "https://files.pythonhosted.org/packages/c3/90/98ef257c23c46425dc4d1d31005ad7c8d649fe423a38b917db02c30f1f5a/shapely-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b510dda1a3672d6879beb319bc7c5fd302c6c354584690973c838f46ec3e0fa8", size = 1832644, upload-time = "2025-09-24T13:50:44.886Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ab/0bee5a830d209adcd3a01f2d4b70e587cdd9fd7380d5198c064091005af8/shapely-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8cff473e81017594d20ec55d86b54bc635544897e13a7cfc12e36909c5309a2a", size = 1642887, upload-time = "2025-09-24T13:50:46.735Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5e/7d7f54ba960c13302584c73704d8c4d15404a51024631adb60b126a4ae88/shapely-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe7b77dc63d707c09726b7908f575fc04ff1d1ad0f3fb92aec212396bc6cfe5e", size = 2970931, upload-time = "2025-09-24T13:50:48.374Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a2/83fc37e2a58090e3d2ff79175a95493c664bcd0b653dd75cb9134645a4e5/shapely-2.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ed1a5bbfb386ee8332713bf7508bc24e32d24b74fc9a7b9f8529a55db9f4ee6", size = 3082855, upload-time = "2025-09-24T13:50:50.037Z" }, + { url = "https://files.pythonhosted.org/packages/44/2b/578faf235a5b09f16b5f02833c53822294d7f21b242f8e2d0cf03fb64321/shapely-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a84e0582858d841d54355246ddfcbd1fce3179f185da7470f41ce39d001ee1af", size = 3979960, upload-time = "2025-09-24T13:50:51.74Z" }, + { url = "https://files.pythonhosted.org/packages/4d/04/167f096386120f692cc4ca02f75a17b961858997a95e67a3cb6a7bbd6b53/shapely-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc3487447a43d42adcdf52d7ac73804f2312cbfa5d433a7d2c506dcab0033dfd", size = 4142851, upload-time = "2025-09-24T13:50:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/48/74/fb402c5a6235d1c65a97348b48cdedb75fb19eca2b1d66d04969fc1c6091/shapely-2.1.2-cp313-cp313-win32.whl", hash = "sha256:9c3a3c648aedc9f99c09263b39f2d8252f199cb3ac154fadc173283d7d111350", size = 1541890, upload-time = "2025-09-24T13:50:55.337Z" }, + { url = "https://files.pythonhosted.org/packages/41/47/3647fe7ad990af60ad98b889657a976042c9988c2807cf322a9d6685f462/shapely-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:ca2591bff6645c216695bdf1614fca9c82ea1144d4a7591a466fef64f28f0715", size = 1722151, upload-time = "2025-09-24T13:50:57.153Z" }, + { url = "https://files.pythonhosted.org/packages/3c/49/63953754faa51ffe7d8189bfbe9ca34def29f8c0e34c67cbe2a2795f269d/shapely-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2d93d23bdd2ed9dc157b46bc2f19b7da143ca8714464249bef6771c679d5ff40", size = 1834130, upload-time = "2025-09-24T13:50:58.49Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ee/dce001c1984052970ff60eb4727164892fb2d08052c575042a47f5a9e88f/shapely-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01d0d304b25634d60bd7cf291828119ab55a3bab87dc4af1e44b07fb225f188b", size = 1642802, upload-time = "2025-09-24T13:50:59.871Z" }, + { url = "https://files.pythonhosted.org/packages/da/e7/fc4e9a19929522877fa602f705706b96e78376afb7fad09cad5b9af1553c/shapely-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8d8382dd120d64b03698b7298b89611a6ea6f55ada9d39942838b79c9bc89801", size = 3018460, upload-time = "2025-09-24T13:51:02.08Z" }, + { url = "https://files.pythonhosted.org/packages/a1/18/7519a25db21847b525696883ddc8e6a0ecaa36159ea88e0fef11466384d0/shapely-2.1.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19efa3611eef966e776183e338b2d7ea43569ae99ab34f8d17c2c054d3205cc0", size = 3095223, upload-time = "2025-09-24T13:51:04.472Z" }, + { url = "https://files.pythonhosted.org/packages/48/de/b59a620b1f3a129c3fecc2737104a0a7e04e79335bd3b0a1f1609744cf17/shapely-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:346ec0c1a0fcd32f57f00e4134d1200e14bf3f5ae12af87ba83ca275c502498c", size = 4030760, upload-time = "2025-09-24T13:51:06.455Z" }, + { url = "https://files.pythonhosted.org/packages/96/b3/c6655ee7232b417562bae192ae0d3ceaadb1cc0ffc2088a2ddf415456cc2/shapely-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6305993a35989391bd3476ee538a5c9a845861462327efe00dd11a5c8c709a99", size = 4170078, upload-time = "2025-09-24T13:51:08.584Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8e/605c76808d73503c9333af8f6cbe7e1354d2d238bda5f88eea36bfe0f42a/shapely-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:c8876673449f3401f278c86eb33224c5764582f72b653a415d0e6672fde887bf", size = 1559178, upload-time = "2025-09-24T13:51:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/36/f7/d317eb232352a1f1444d11002d477e54514a4a6045536d49d0c59783c0da/shapely-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:4a44bc62a10d84c11a7a3d7c1c4fe857f7477c3506e24c9062da0db0ae0c449c", size = 1739756, upload-time = "2025-09-24T13:51:12.105Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9a522f460d28e2bf4e12396240a5fc1518788b2fcd73535166d748399ef0c223", size = 1831290, upload-time = "2025-09-24T13:51:13.56Z" }, + { url = "https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ff629e00818033b8d71139565527ced7d776c269a49bd78c9df84e8f852190c", size = 1641463, upload-time = "2025-09-24T13:51:14.972Z" }, + { url = "https://files.pythonhosted.org/packages/a5/57/91d59ae525ca641e7ac5551c04c9503aee6f29b92b392f31790fcb1a4358/shapely-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f67b34271dedc3c653eba4e3d7111aa421d5be9b4c4c7d38d30907f796cb30df", size = 2970145, upload-time = "2025-09-24T13:51:16.961Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21952dc00df38a2c28375659b07a3979d22641aeb104751e769c3ee825aadecf", size = 3073806, upload-time = "2025-09-24T13:51:18.712Z" }, + { url = "https://files.pythonhosted.org/packages/03/83/f768a54af775eb41ef2e7bec8a0a0dbe7d2431c3e78c0a8bdba7ab17e446/shapely-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f2f33f486777456586948e333a56ae21f35ae273be99255a191f5c1fa302eb4", size = 3980803, upload-time = "2025-09-24T13:51:20.37Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/559c7c195807c91c79d38a1f6901384a2878a76fbdf3f1048893a9b7534d/shapely-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cf831a13e0d5a7eb519e96f58ec26e049b1fad411fc6fc23b162a7ce04d9cffc", size = 4133301, upload-time = "2025-09-24T13:51:21.887Z" }, + { url = "https://files.pythonhosted.org/packages/80/cd/60d5ae203241c53ef3abd2ef27c6800e21afd6c94e39db5315ea0cbafb4a/shapely-2.1.2-cp314-cp314-win32.whl", hash = "sha256:61edcd8d0d17dd99075d320a1dd39c0cb9616f7572f10ef91b4b5b00c4aeb566", size = 1583247, upload-time = "2025-09-24T13:51:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:a444e7afccdb0999e203b976adb37ea633725333e5b119ad40b1ca291ecf311c", size = 1773019, upload-time = "2025-09-24T13:51:24.873Z" }, + { url = "https://files.pythonhosted.org/packages/a3/05/a44f3f9f695fa3ada22786dc9da33c933da1cbc4bfe876fe3a100bafe263/shapely-2.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5ebe3f84c6112ad3d4632b1fd2290665aa75d4cef5f6c5d77c4c95b324527c6a", size = 1834137, upload-time = "2025-09-24T13:51:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/4d57db45bf314573427b0a70dfca15d912d108e6023f623947fa69f39b72/shapely-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5860eb9f00a1d49ebb14e881f5caf6c2cf472c7fd38bd7f253bbd34f934eb076", size = 1642884, upload-time = "2025-09-24T13:51:28.029Z" }, + { url = "https://files.pythonhosted.org/packages/5a/27/4e29c0a55d6d14ad7422bf86995d7ff3f54af0eba59617eb95caf84b9680/shapely-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b705c99c76695702656327b819c9660768ec33f5ce01fa32b2af62b56ba400a1", size = 3018320, upload-time = "2025-09-24T13:51:29.903Z" }, + { url = "https://files.pythonhosted.org/packages/9f/bb/992e6a3c463f4d29d4cd6ab8963b75b1b1040199edbd72beada4af46bde5/shapely-2.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1fd0ea855b2cf7c9cddaf25543e914dd75af9de08785f20ca3085f2c9ca60b0", size = 3094931, upload-time = "2025-09-24T13:51:32.699Z" }, + { url = "https://files.pythonhosted.org/packages/9c/16/82e65e21070e473f0ed6451224ed9fa0be85033d17e0c6e7213a12f59d12/shapely-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:df90e2db118c3671a0754f38e36802db75fe0920d211a27481daf50a711fdf26", size = 4030406, upload-time = "2025-09-24T13:51:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/7c/75/c24ed871c576d7e2b64b04b1fe3d075157f6eb54e59670d3f5ffb36e25c7/shapely-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:361b6d45030b4ac64ddd0a26046906c8202eb60d0f9f53085f5179f1d23021a0", size = 4169511, upload-time = "2025-09-24T13:51:36.297Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f7/b3d1d6d18ebf55236eec1c681ce5e665742aab3c0b7b232720a7d43df7b6/shapely-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:b54df60f1fbdecc8ebc2c5b11870461a6417b3d617f555e5033f1505d36e5735", size = 1602607, upload-time = "2025-09-24T13:51:37.757Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f6/f09272a71976dfc138129b8faf435d064a811ae2f708cb147dccdf7aacdb/shapely-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0036ac886e0923417932c2e6369b6c52e38e0ff5d9120b90eef5cd9a5fc5cae9", size = 1796682, upload-time = "2025-09-24T13:51:39.233Z" }, ] [[package]] @@ -1272,45 +1353,45 @@ wheels = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20250516" +version = "6.0.12.20250915" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378, upload-time = "2025-05-16T03:08:04.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/69/3c51b36d04da19b92f9e815be12753125bd8bc247ba0470a982e6979e71c/types_pyyaml-6.0.12.20250915.tar.gz", hash = "sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3", size = 17522, upload-time = "2025-09-15T03:01:00.728Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312, upload-time = "2025-05-16T03:08:04.019Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e0/1eed384f02555dde685fff1a1ac805c1c7dcb6dd019c916fe659b1c1f9ec/types_pyyaml-6.0.12.20250915-py3-none-any.whl", hash = "sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6", size = 20338, upload-time = "2025-09-15T03:00:59.218Z" }, ] [[package]] name = "types-shapely" -version = "2.1.0.20250710" +version = "2.1.0.20250917" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/70/37f63bc62aefde18795bd807381f7e2c2b7884f7f1ddaed2e6f727a099f9/types_shapely-2.1.0.20250710.tar.gz", hash = "sha256:f93067996f5203dffe57f57a130ae757b7e83609b046ea9631de8b900e4be77b", size = 26205, upload-time = "2025-07-10T03:15:50.902Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/19/7f28b10994433d43b9caa66f3b9bd6a0a9192b7ce8b5a7fc41534e54b821/types_shapely-2.1.0.20250917.tar.gz", hash = "sha256:5c56670742105aebe40c16414390d35fcaa55d6f774d328c1a18273ab0e2134a", size = 26363, upload-time = "2025-09-17T02:47:44.604Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/f1/bc36b6a7dbe742b0dc75c0fe6b4f27a2a37dedcf63bac9afe55bdb998870/types_shapely-2.1.0.20250710-py3-none-any.whl", hash = "sha256:cf58a2f6a79ec7cbdaf2ba8a8d744842bd1f5ae159afc2512db552b2c67219f4", size = 37696, upload-time = "2025-07-10T03:15:49.852Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a9/554ac40810e530263b6163b30a2b623bc16aae3fb64416f5d2b3657d0729/types_shapely-2.1.0.20250917-py3-none-any.whl", hash = "sha256:9334a79339504d39b040426be4938d422cec419168414dc74972aa746a8bf3a1", size = 37813, upload-time = "2025-09-17T02:47:43.788Z" }, ] [[package]] name = "typing-extensions" -version = "4.14.1" +version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] name = "typing-inspection" -version = "0.4.1" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]] From 7da3ea43574a2ab7a829b9351bef034ad8f52a4c Mon Sep 17 00:00:00 2001 From: schapper Date: Thu, 2 Oct 2025 17:19:35 -0700 Subject: [PATCH 18/20] wip - Remove GERS stuff from diff --- gers/examples/python/constants.py | 25 +- gers/examples/python/match_classes.py | 164 +---- gers/examples/python/match_traces.ipynb | 79 +-- gers/examples/python/match_traces.py | 640 ++++-------------- gers/examples/python/route_utils.py | 78 +-- .../python/tests/match_traces_test.py | 38 +- gers/examples/python/tests/test_setup.py | 4 +- gers/examples/python/tests/utils_test.py | 62 +- gers/examples/python/utils.py | 174 ++--- 9 files changed, 327 insertions(+), 937 deletions(-) diff --git a/gers/examples/python/constants.py b/gers/examples/python/constants.py index 34776bb74..f15adb2d0 100644 --- a/gers/examples/python/constants.py +++ b/gers/examples/python/constants.py @@ -1,24 +1,27 @@ +from enum import Enum +import os + DEFAULT_H3_RESOLUTION = 12 # default params for nearest match -DEFAULT_NEAREST_MAX_DISTANCE = 100 # meters +DEFAULT_NEAREST_MAX_DISTANCE = 100 # meters # default params for trace snapping -DEFAULT_SIGMA = 4.1 # 4.10351310622546; -DEFAULT_BETA = 0.9 # 0.905918746744877 -> this default beta was found to apply to a 5 second sample rate. +DEFAULT_SIGMA = 4.1 # 4.10351310622546; +DEFAULT_BETA = 0.9 # 0.905918746744877 -> this default beta was found to apply to a 5 second sample rate. # also was found to have good noise rejection characteristics and performed just as well or better than 1 second data, so it # is now our default sampling period - even if the raw data was sampled at a higher rate -DEFAULT_MAX_POINT_TO_ROAD_DISTANCE = 10 # 200m in original paper -DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE = ( - 300 # what's a good value for this? 2km in original paper but too slow -) +DEFAULT_MAX_POINT_TO_ROAD_DISTANCE = 10 # 200m in original paper +DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE = 300 # what's a good value for this? 2km in original paper but too slow DEFAULT_ALLOW_LOOPS = False -DEFAULT_SEGMENT_REVISIT_PENALTY = 100 # set to 0 if no penalty is desired -DEFAULT_VIA_POINT_PENALTY_WEIGHT = 100 # set to 0 if no penalty is desired -DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE = 60 # seconds -DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE = 300 # meters +DEFAULT_SEGMENT_REVISIT_PENALTY = 100 # set to 0 if no penalty is desired +DEFAULT_VIA_POINT_PENALTY_WEIGHT = 100 # set to 0 if no penalty is desired +DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE = 60 # seconds +DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE = 300 # meters """default column separator of text files""" COLUMN_SEPARATOR = "\t" DATA_DIR = "gers/examples/python/data" + + diff --git a/gers/examples/python/match_classes.py b/gers/examples/python/match_classes.py index 05bfae56b..0e1c55805 100644 --- a/gers/examples/python/match_classes.py +++ b/gers/examples/python/match_classes.py @@ -1,67 +1,39 @@ import json -from collections.abc import Iterable - -import constants +from typing import Dict, Iterable from shapely.geometry import Point from shapely.geometry.base import BaseGeometry - +import constants class MatchableFeature: """ Convenience class to hold an id, a shapely geometry, and optionally a dictionary of properties for use in matching. It can be trivially populated from geojson and overture as an extension of geojson. """ - - def __init__( - self, id: str, geometry: BaseGeometry, properties: dict = None - ) -> None: + def __init__(self, id: str, geometry:BaseGeometry, properties: dict=None) -> None: self.id = str(id) self.geometry = geometry self.properties = properties def __str__(self) -> str: - return json.dumps( - { - "id": self.id, - "geometry": self.geometry.wkt, - "properties": self.properties, - } - ) + return json.dumps({ + "id": self.id, + "geometry": self.geometry.wkt, + "properties": self.properties + }) def get_connector_ids(self) -> Iterable[str]: - return ( - self.properties["connector_ids"] - if self.properties is not None and "connector_ids" in self.properties - else [] - ) - + return self.properties["connector_ids"] if self.properties is not None and "connector_ids" in self.properties else [] class MatchableFeaturesSet: """Collection of matchable features, indexed by id, and by cells (H3 in current implementation)""" - - def __init__( - self, - features: dict[str, Iterable[MatchableFeature]], - cells_by_id: dict[str, Iterable[str]], - features_by_cell: dict[str, Iterable[MatchableFeature]], - ) -> None: + def __init__(self, features: Dict[str, Iterable[MatchableFeature]], cells_by_id: Dict[str, Iterable[str]], features_by_cell: Dict[str, Iterable[MatchableFeature]]) -> None: self.features_by_id = features self.cells_by_id = cells_by_id self.features_by_cell = features_by_cell - class MatchedFeature: """One matched feature with match-relevant information""" - - def __init__( - self, - id: str, - matched_feature: MatchableFeature, - overlapping_geometry: BaseGeometry, - score: float, - source_lr: Iterable[float] = None, - candidate_lr: Iterable[float] = None, - ) -> None: + def __init__(self, id: str, matched_feature: MatchableFeature, overlapping_geometry: BaseGeometry, score: float, source_lr: Iterable[float]=None, candidate_lr: Iterable[float]=None) -> None: """ Attributes: id: the gers id of the matched feature @@ -71,7 +43,7 @@ def __init__( source_lr: the Location Reference in the source geometry of the part that matched as array of from-to points projection factors candidate_lr: the Location Reference in the matched geometry of the part that matched the source geometry """ - self.id = id # the gers id of the matched feature + self.id = id # the gers id of the matched feature self.matched_feature = matched_feature self.overlapping_geometry = overlapping_geometry self.score = score @@ -82,9 +54,7 @@ def to_json(self): j = { "id": str(self.id), "candidate_wkt": self.matched_feature.geometry.wkt, - "overlapping_wkt": self.overlapping_geometry.wkt - if self.overlapping_geometry is not None - else None, + "overlapping_wkt": self.overlapping_geometry.wkt if self.overlapping_geometry is not None else None, "score": self.score, } if self.source_lr is not None: @@ -96,38 +66,30 @@ def to_json(self): def __str__(self) -> str: return json.dumps(self.to_json()) - class TraceSnapOptions: - """ "Parameters for matching a trace to road segments""" - - def __init__( - self, - sigma=constants.DEFAULT_SIGMA, - beta=constants.DEFAULT_BETA, - max_point_to_road_distance=constants.DEFAULT_MAX_POINT_TO_ROAD_DISTANCE, - max_route_to_trace_distance_difference=constants.DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE, - allow_loops=constants.DEFAULT_ALLOW_LOOPS, - revisit_segment_penalty_weight=constants.DEFAULT_SEGMENT_REVISIT_PENALTY, - revisit_via_point_penalty_weight=constants.DEFAULT_VIA_POINT_PENALTY_WEIGHT, - broken_time_gap_reset_sequence=constants.DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE, - broken_distance_gap_reset_sequence=constants.DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE, - ) -> None: + """"Parameters for matching a trace to road segments""" + def __init__(self, \ + sigma=constants.DEFAULT_SIGMA,\ + beta=constants.DEFAULT_BETA,\ + max_point_to_road_distance=constants.DEFAULT_MAX_POINT_TO_ROAD_DISTANCE,\ + max_route_to_trace_distance_difference=constants.DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE,\ + allow_loops=constants.DEFAULT_ALLOW_LOOPS, + revisit_segment_penalty_weight=constants.DEFAULT_SEGMENT_REVISIT_PENALTY, + revisit_via_point_penalty_weight=constants.DEFAULT_VIA_POINT_PENALTY_WEIGHT, + broken_time_gap_reset_sequence=constants.DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE, + broken_distance_gap_reset_sequence=constants.DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE) -> None: self.sigma = sigma self.beta = beta self.allow_loops = allow_loops self.max_point_to_road_distance = max_point_to_road_distance - self.max_route_to_trace_distance_difference = ( - max_route_to_trace_distance_difference - ) + self.max_route_to_trace_distance_difference = max_route_to_trace_distance_difference self.revisit_segment_penalty_weight = revisit_segment_penalty_weight self.revisit_via_point_penalty_weight = revisit_via_point_penalty_weight self.broken_time_gap_reset_sequence = broken_time_gap_reset_sequence self.broken_distance_gap_reset_sequence = broken_distance_gap_reset_sequence - class RouteStep: """One step in a route, corresponding to one road segment feature""" - def __init__(self, feature: MatchableFeature, via_point: Point) -> None: """ Attributes: @@ -137,34 +99,15 @@ def __init__(self, feature: MatchableFeature, via_point: Point) -> None: self.feature = feature self.via_point = via_point - class Route: """A route, consisting of a sequence of steps""" - def __init__(self, distance: float, steps: Iterable[RouteStep]) -> None: self.distance = distance self.steps = steps - class SnappedPointPrediction: """A road segment feature as a snap prediction for point in a trace, with relevant match signals""" - - def __init__( - self, - id: str, - snapped_point: Point, - referenced_feature: MatchableFeature, - distance_to_snapped_road: float, - route_distance_to_prev_point: float, - emission_prob: float, - best_transition_prob: float, - best_log_prob: float, - best_prev_prediction: float, - best_sequence: Iterable[str], - best_route_via_points: Iterable[str], - best_revisited_via_points_count: int, - best_revisited_segments_count: int, - ) -> None: + def __init__(self, id: str, snapped_point: Point, referenced_feature: MatchableFeature, distance_to_snapped_road: float, route_distance_to_prev_point: float, emission_prob: float, best_transition_prob: float, best_log_prob: float, best_prev_prediction: float, best_sequence: Iterable[str], best_route_via_points: Iterable[str], best_revisited_via_points_count:int, best_revisited_segments_count:int) -> None: self.id = str(id) self.snapped_point = snapped_point self.referenced_feature = referenced_feature @@ -203,18 +146,9 @@ def to_json(self, diagnostic_mode=False): return j - class PointSnapInfo: """Snap-to-road match information corresponding to one point in a trace""" - - def __init__( - self, - index: int, - original_point: Point, - time: str, - seconds_since_prev_point: float = None, - predictions: Iterable[SnappedPointPrediction] = [], - ) -> None: + def __init__(self, index: int, original_point: Point, time: str, seconds_since_prev_point: float=None, predictions:Iterable[SnappedPointPrediction]=[]) -> None: self.index = index self.original_point = original_point self.time = time @@ -223,16 +157,8 @@ def __init__( self.best_prediction = None self.ignore = False - def to_json( - self, - diagnostic_mode: bool = False, - include_all_predictions: bool = False, - ): - best_prediction_json = ( - None - if self.best_prediction is None - else self.best_prediction.to_json(diagnostic_mode) - ) + def to_json(self, diagnostic_mode: bool=False, include_all_predictions: bool=False,): + best_prediction_json = None if self.best_prediction is None else self.best_prediction.to_json(diagnostic_mode) j = { "original_point": self.original_point.wkt, @@ -248,31 +174,12 @@ def to_json( j["point_index"] = self.index if include_all_predictions: - j["predictions"] = list( - map(lambda x: x.to_json(diagnostic_mode), self.predictions) - ) + j["predictions"] = list(map(lambda x: x.to_json(diagnostic_mode), self.predictions)) return j - class TraceMatchResult: """Result of a matching trace to road segments""" - - def __init__( - self, - id: str, - source_wkt: str, - points: Iterable[PointSnapInfo], - source_length: float, - target_candidates_count: int, - matched_target_ids: Iterable[str] = None, - elapsed: float = None, - sequence_breaks: int = 0, - points_with_matches: int = 0, - route_length: float = 0, - avg_dist_to_road: float = None, - revisited_via_points: int = 0, - revisited_segments: int = 0, - ) -> None: + def __init__(self, id: str, source_wkt: str, points: Iterable[PointSnapInfo], source_length: float, target_candidates_count: int, matched_target_ids: Iterable[str]=None, elapsed: float=None, sequence_breaks: int=0, points_with_matches: int=0, route_length: float=0, avg_dist_to_road: float=None, revisited_via_points: int=0, revisited_segments: int=0) -> None: self.id = id self.source_wkt = source_wkt self.points = points @@ -288,12 +195,7 @@ def __init__( self.revisited_segments = revisited_segments def to_json(self, diagnostic_mode=False, include_all_predictions=False): - points_json = list( - map( - lambda x: x.to_json(diagnostic_mode, include_all_predictions), - self.points, - ) - ) + points_json = list(map(lambda x: x.to_json(diagnostic_mode, include_all_predictions), self.points)) return { "id": str(self.id), "elapsed": self.elapsed, @@ -307,7 +209,7 @@ def to_json(self, diagnostic_mode=False, include_all_predictions=False): "revisited_segments": self.revisited_segments, "target_candidates_count": self.target_candidates_count, "target_ids": self.matched_target_ids, - "points": points_json, + "points": points_json } def __str__(self) -> str: diff --git a/gers/examples/python/match_traces.ipynb b/gers/examples/python/match_traces.ipynb index 8f8618a0a..413f761fd 100644 --- a/gers/examples/python/match_traces.ipynb +++ b/gers/examples/python/match_traces.ipynb @@ -158,21 +158,18 @@ } ], "source": [ + "\n", "to_match_gdf = gpd.read_file(input_to_match_file)\n", "to_match_gdf.crs = \"epsg:4326\"\n", "\n", "# add column \"id_to_match\"; this will be used to group all match candidates for a feature, so it must be unique within your data set.\n", "# if your data doesn't have an id column, you can use to_match_gdf.index for example\n", - "to_match_gdf[\"id_to_match\"] = to_match_gdf[\"id\"]\n", + "to_match_gdf[\"id_to_match\"] = to_match_gdf[\"id\"] \n", "property_columns = to_match_gdf.columns.difference([\"geometry\", \"id\", \"type\"])\n", - "to_match_gdf[\"properties\"] = to_match_gdf[property_columns].apply(\n", - " lambda x: x.to_dict(), axis=1\n", - ")\n", + "to_match_gdf[\"properties\"] = to_match_gdf[property_columns].apply(lambda x: x.to_dict(), axis=1)\n", "\n", "# construct a MatchableFeature object for each feature\n", - "to_match_gdf[\"feature_to_match\"] = to_match_gdf.apply(\n", - " lambda row: MatchableFeature(row.id, row.geometry, row.properties), axis=1\n", - ")\n", + "to_match_gdf[\"feature_to_match\"] = to_match_gdf.apply(lambda row: MatchableFeature(row.id, row.geometry, row.properties), axis=1)\n", "\n", "to_match_gdf.head(3)" ] @@ -300,16 +297,12 @@ "overture_gdf = gpd.read_file(input_overture_file)\n", "overture_gdf.crs = \"epsg:4326\"\n", "\n", - "# combine properties into a single column\n", + "# combine properties into a single column \n", "property_columns = overture_gdf.columns.difference([\"geometry\", \"id\", \"type\"])\n", - "overture_gdf[\"properties\"] = overture_gdf[property_columns].apply(\n", - " lambda x: x.to_dict(), axis=1\n", - ")\n", + "overture_gdf[\"properties\"] = overture_gdf[property_columns].apply(lambda x: x.to_dict(), axis=1)\n", "\n", "# construct a MatchableFeature object for each feature\n", - "overture_gdf[\"candidate_feature\"] = overture_gdf.apply(\n", - " lambda row: MatchableFeature(row.id, row.geometry, row.properties), axis=1\n", - ")\n", + "overture_gdf[\"candidate_feature\"] = overture_gdf.apply(lambda row: MatchableFeature(row.id, row.geometry, row.properties), axis=1)\n", "\n", "overture_gdf.head(3)" ] @@ -517,14 +510,10 @@ "to_match_utm_gdf[\"original_geometry\"] = to_match_utm_gdf[\"geometry\"].copy(deep=True)\n", "\n", "# add 20 meters buffer to the features to match, to account for the fact that the overture features are not perfectly aligned with the features to be matched\n", - "to_match_utm_gdf[\"geometry\"] = to_match_utm_gdf[\"original_geometry\"].buffer(\n", - " 20, cap_style=2\n", - ")\n", + "to_match_utm_gdf[\"geometry\"] = to_match_utm_gdf[\"original_geometry\"].buffer(20, cap_style=2)\n", "\n", "# spatial join between points and segments - get nearest overture feature, where distance < 100 meters\n", - "joined_gdf = sjoin(\n", - " to_match_utm_gdf, candidates_utm_gdf, how=\"left\", predicate=\"intersects\"\n", - ")\n", + "joined_gdf = sjoin(to_match_utm_gdf, candidates_utm_gdf, how=\"left\", predicate=\"intersects\")\n", "joined_gdf.head(3)" ] }, @@ -601,9 +590,7 @@ ], "source": [ "# group join results by id_to_match, and aggregate the candidate features into a list\n", - "grouped_df = joined_gdf.groupby([\"id_to_match\"]).agg(\n", - " {\"feature_to_match\": \"first\", \"candidate_feature\": lambda x: list(x)}\n", - ")\n", + "grouped_df = joined_gdf.groupby([\"id_to_match\"]).agg({\"feature_to_match\": \"first\", \"candidate_feature\": lambda x: list(x)})\n", "grouped_df = grouped_df.reset_index()\n", "grouped_df.head(3)" ] @@ -690,11 +677,8 @@ ], "source": [ "# run the trace matching algorithm for each trace feature to match\n", - "options = TraceSnapOptions(max_point_to_road_distance=10)\n", - "grouped_df[\"match_result\"] = grouped_df.apply(\n", - " lambda row: get_trace_matches(row.feature_to_match, row.candidate_feature, options),\n", - " axis=1,\n", - ")\n", + "options = TraceSnapOptions(max_point_to_road_distance=10) \n", + "grouped_df[\"match_result\"] = grouped_df.apply(lambda row: get_trace_matches(row.feature_to_match, row.candidate_feature, options), axis=1)\n", "grouped_df.head(3)" ] }, @@ -706,9 +690,8 @@ "source": [ "# save the results to a file, result as one json line per feature to match\n", "import numpy as np\n", - "\n", "results_df = grouped_df.apply(lambda x: x.match_result.to_json(), axis=1)\n", - "np.savetxt(output_file, results_df.values, fmt=\"%s\")" + "np.savetxt(output_file, results_df.values, fmt='%s')" ] }, { @@ -852,34 +835,16 @@ ], "source": [ "# add some of the metrics from the result object as columns for analysis\n", - "grouped_df[\"source_length\"] = grouped_df.apply(\n", - " lambda x: x.match_result.source_length, axis=1\n", - ")\n", - "grouped_df[\"route_length\"] = grouped_df.apply(\n", - " lambda x: x.match_result.route_length, axis=1\n", - ")\n", + "grouped_df[\"source_length\"] = grouped_df.apply(lambda x: x.match_result.source_length, axis=1)\n", + "grouped_df[\"route_length\"] = grouped_df.apply(lambda x: x.match_result.route_length, axis=1)\n", "grouped_df[\"points\"] = grouped_df.apply(lambda x: len(x.match_result.points), axis=1)\n", - "grouped_df[\"points_with_matches\"] = grouped_df.apply(\n", - " lambda x: x.match_result.points_with_matches, axis=1\n", - ")\n", - "grouped_df[\"avg_dist_to_road\"] = grouped_df.apply(\n", - " lambda x: x.match_result.avg_dist_to_road, axis=1\n", - ")\n", - "grouped_df[\"sequence_breaks\"] = grouped_df.apply(\n", - " lambda x: x.match_result.sequence_breaks, axis=1\n", - ")\n", - "grouped_df[\"revisited_via_points\"] = grouped_df.apply(\n", - " lambda x: x.match_result.revisited_via_points, axis=1\n", - ")\n", - "grouped_df[\"revisited_segments\"] = grouped_df.apply(\n", - " lambda x: x.match_result.revisited_segments, axis=1\n", - ")\n", - "grouped_df[\"candidates_count\"] = grouped_df.apply(\n", - " lambda x: x.match_result.target_candidates_count, axis=1\n", - ")\n", - "grouped_df[\"matched_segments\"] = grouped_df.apply(\n", - " lambda x: len(x.match_result.matched_target_ids), axis=1\n", - ")\n", + "grouped_df[\"points_with_matches\"] = grouped_df.apply(lambda x: x.match_result.points_with_matches, axis=1)\n", + "grouped_df[\"avg_dist_to_road\"] = grouped_df.apply(lambda x: x.match_result.avg_dist_to_road, axis=1)\n", + "grouped_df[\"sequence_breaks\"] = grouped_df.apply(lambda x: x.match_result.sequence_breaks, axis=1)\n", + "grouped_df[\"revisited_via_points\"] = grouped_df.apply(lambda x: x.match_result.revisited_via_points, axis=1)\n", + "grouped_df[\"revisited_segments\"] = grouped_df.apply(lambda x: x.match_result.revisited_segments, axis=1)\n", + "grouped_df[\"candidates_count\"] = grouped_df.apply(lambda x: x.match_result.target_candidates_count, axis=1)\n", + "grouped_df[\"matched_segments\"] = grouped_df.apply(lambda x: len(x.match_result.matched_target_ids), axis=1)\n", "grouped_df[\"elapsed\"] = grouped_df.apply(lambda x: x.match_result.elapsed, axis=1)\n", "grouped_df.head(3)" ] diff --git a/gers/examples/python/match_traces.py b/gers/examples/python/match_traces.py index 8b55cde97..bbf2e6a02 100644 --- a/gers/examples/python/match_traces.py +++ b/gers/examples/python/match_traces.py @@ -1,40 +1,25 @@ import argparse import csv import json -import math import os -from collections.abc import Iterable -from timeit import default_timer as timer +import math import constants -from match_classes import ( - MatchableFeature, - PointSnapInfo, - RouteStep, - SnappedPointPrediction, - TraceMatchResult, - TraceSnapOptions, -) from route_utils import get_shortest_route +from match_classes import TraceSnapOptions, MatchableFeature, TraceMatchResult, SnappedPointPrediction, PointSnapInfo, RouteStep +from utils import get_features_with_cells, get_seconds_elapsed, get_distance, get_linestring_length, load_matchable_set + from shapely import Point from shapely.ops import nearest_points -from utils import ( - get_distance, - get_features_with_cells, - get_linestring_length, - get_seconds_elapsed, - load_matchable_set, -) - - -def get_feature_id_to_connected_features( - features_overture: Iterable[MatchableFeature], -) -> dict[str, Iterable[MatchableFeature]]: +from timeit import default_timer as timer +from typing import Dict, Iterable + +def get_feature_id_to_connected_features(features_overture: Iterable[MatchableFeature]) -> Dict[str, Iterable[MatchableFeature]]: """returns a connected roads "graph" as a dictionary of feature id to features that are connected to it, as modeled in overture schema via connector_ids property""" connector_id_to_features = {} for feature in features_overture: for connector_id in feature.get_connector_ids(): - if connector_id not in connector_id_to_features: + if not connector_id in connector_id_to_features: connector_id_to_features[connector_id] = [] connector_id_to_features[connector_id].append(feature) @@ -47,56 +32,36 @@ def get_feature_id_to_connected_features( feature_id_to_connected_features[feature.id].append(other_feature) return feature_id_to_connected_features - def read_predictions(predictions_file: str): """reads snap predictions from tab separated file with columns: trace_id, point_index, gers_id, score""" p = {} - with open(predictions_file) as file: + with open(predictions_file, 'r') as file: reader = csv.reader(file, delimiter=constants.COLUMN_SEPARATOR) for row in reader: try: trace_id = row[0] point_index = int(row[1]) gers_id = row[3] - if trace_id not in p: + if not(trace_id in p): p[trace_id] = {} p[trace_id][point_index] = gers_id except ValueError: - continue # header or invalid line + continue # header or invalid line return p - -def calculate_error_rate( - labeled_file: str, - target_features_by_id: dict[str, Iterable[MatchableFeature]], - match_results: Iterable[TraceMatchResult], -): +def calculate_error_rate(labeled_file: str, target_features_by_id: Dict[str, Iterable[MatchableFeature]], match_results: Iterable[TraceMatchResult]): """returns total error rate from a labeled file and a list of trace match results""" - if not (os.path.exists(labeled_file)): - print(f"no metrics to compute (file {labeled_file} does not exist)") + if not(os.path.exists(labeled_file)): + print(f'no metrics to compute (file {labeled_file} does not exist)') return labels = read_predictions(labeled_file) total_correct_distance = 0 total_incorrect_distance = 0 - with open(labeled_file + ".actual.txt", "w") as f: - f.write( - constants.COLUMN_SEPARATOR.join( - [ - "trace_id", - "point_index", - "label_gers_id", - "prediction_gers_id", - "label_snapped_wkt", - "prediction_snapped_wkt", - "distance_to_prev_point", - "is_correct", - ] - ) - + "\n" - ) + with open(labeled_file + ".actual.txt",'w') as f: + f.write(constants.COLUMN_SEPARATOR.join(["trace_id", "point_index", "label_gers_id", "prediction_gers_id", "label_snapped_wkt", "prediction_snapped_wkt", "distance_to_prev_point", "is_correct"]) + "\n") for trace_match_result in match_results: - if trace_match_result.id not in labels: + if not(trace_match_result.id in labels): continue correct_distance = 0 @@ -104,17 +69,13 @@ def calculate_error_rate( prev_point = None for point in trace_match_result.points: - if point.index not in labels[trace_match_result.id]: - print( - f"no label for trace_id={trace_match_result.id} point_index={point.index}" - ) + if not(point.index in labels[trace_match_result.id]): + print(f'no label for trace_id={trace_match_result.id} point_index={point.index}') break label_gers_id = labels[trace_match_result.id][point.index] dist_to_prev_point = 0 - is_correct = point.best_prediction is not None and ( - str(point.best_prediction.id) == label_gers_id - ) + is_correct = not(point.best_prediction is None) and (str(point.best_prediction.id) == label_gers_id) if prev_point is not None: dist_to_prev_point = get_distance(prev_point, point.original_point) correct_distance += dist_to_prev_point @@ -127,124 +88,78 @@ def calculate_error_rate( incorrect_distance += dist_to_prev_point label_snapped_point = None - if label_gers_id not in target_features_by_id: - print(f"no target feature for label_gers_id={label_gers_id}") + if not(label_gers_id in target_features_by_id): + print(f'no target feature for label_gers_id={label_gers_id}') else: label_shape = target_features_by_id[label_gers_id].geometry - x, label_snapped_point = nearest_points( - point.original_point, label_shape - ) - - columns = [ - str(trace_match_result.id), - str(point.index), - str(label_gers_id), - str(point.best_prediction.id) - if point.best_prediction is not None - else "", - label_snapped_point.wkt if label_snapped_point is not None else "", - point.best_prediction.snapped_point.wkt - if point.best_prediction is not None - else "", - str(dist_to_prev_point), - str(is_correct), - ] + x, label_snapped_point = nearest_points(point.original_point, label_shape) + + columns = [\ + str(trace_match_result.id), \ + str(point.index), \ + str(label_gers_id), \ + str(point.best_prediction.id) if point.best_prediction is not None else "", \ + label_snapped_point.wkt if label_snapped_point is not None else "", \ + point.best_prediction.snapped_point.wkt if point.best_prediction is not None else "", \ + str(dist_to_prev_point), \ + str(is_correct), \ + ] f.write(constants.COLUMN_SEPARATOR.join(columns) + "\n") prev_point = point.original_point trace_error_rate = incorrect_distance / correct_distance - print( - rf"trace_id={trace_match_result.id} trace_error_rate={trace_error_rate:.2f} correct_distance={correct_distance:.2f} incorrect_distance={incorrect_distance:.2f}" - ) + print(rf"trace_id={trace_match_result.id} trace_error_rate={trace_error_rate:.2f} correct_distance={correct_distance:.2f} incorrect_distance={incorrect_distance:.2f}") total_correct_distance += correct_distance total_incorrect_distance += incorrect_distance if total_correct_distance == 0: - print("no correct distance") + print('no correct distance') return -1 total_error_rate = total_incorrect_distance / total_correct_distance - print( - rf"total_error_rate={total_error_rate:.2f} total_correct_distance={total_correct_distance:.2f} total_incorrect_distance={total_incorrect_distance:.2f}" - ) + print(rf"total_error_rate={total_error_rate:.2f} total_correct_distance={total_correct_distance:.2f} total_incorrect_distance={total_incorrect_distance:.2f}") return total_error_rate - -def output_trace_snap_results( - match_results: Iterable[TraceMatchResult], - output_file_name: str, - output_for_judgment: bool = False, -): - results_json = list( - map( - lambda x: x.to_json(diagnostic_mode=False, include_all_predictions=False), - match_results, - ) - ) - with open(output_file_name, "w") as f: +def output_trace_snap_results(match_results: Iterable[TraceMatchResult], output_file_name: str, output_for_judgment: bool = False): + results_json = list(map(lambda x: x.to_json(diagnostic_mode=False, include_all_predictions=False), match_results)) + with open(output_file_name, 'w') as f: json.dump(results_json, f, indent=4) - results_json = list( - map( - lambda x: x.to_json(diagnostic_mode=True, include_all_predictions=False), - match_results, - ) - ) - with open(output_file_name + ".with_diagnostics.json", "w") as f: + results_json = list(map(lambda x: x.to_json(diagnostic_mode=True, include_all_predictions=False), match_results)) + with open(output_file_name + ".with_diagnostics.json", 'w') as f: json.dump(results_json, f, indent=4) - results_json = list( - map( - lambda x: x.to_json(diagnostic_mode=True, include_all_predictions=True), - match_results, - ) - ) - with open(output_file_name + ".with_diagnostics-all-predictions.json", "w") as f: + results_json = list(map(lambda x: x.to_json(diagnostic_mode=True, include_all_predictions=True), match_results)) + with open(output_file_name + ".with_diagnostics-all-predictions.json", 'w') as f: json.dump(results_json, f, indent=4) if output_for_judgment: - with open(output_file_name + ".for_judgment.txt", "w") as f: - f.write( - constants.COLUMN_SEPARATOR.join( - ["trace_id", "point_index", "trace_point_wkt", "gers_id"] - ) - + "\n" - ) + with open(output_file_name + ".for_judgment.txt",'w') as f: + f.write(constants.COLUMN_SEPARATOR.join(["trace_id", "point_index", "trace_point_wkt", "gers_id"]) + "\n") for r in match_results: for idx, p in enumerate(r.points): columns = [ str(r.id), str(idx), p.original_point.wkt, - str(p.best_prediction.id) - if p.best_prediction is not None - else "", + str(p.best_prediction.id) if p.best_prediction is not None else "" ] f.write(constants.COLUMN_SEPARATOR.join(columns) + "\n") - with open(output_file_name + ".snapped_points.txt", "w") as f: - f.write( - constants.COLUMN_SEPARATOR.join( - ["trace_id", "point_index", "gers_id", "snapped_point_wkt"] - ) - + "\n" - ) + with open(output_file_name + ".snapped_points.txt",'w') as f: + f.write(constants.COLUMN_SEPARATOR.join(["trace_id", "point_index", "gers_id", "snapped_point_wkt"]) + "\n") for r in match_results: for idx, p in enumerate(r.points): columns = [ str(r.id), str(idx), - str(p.best_prediction.id) - if p.best_prediction is not None - else "", - p.best_prediction.snapped_point.wkt - if p.best_prediction is not None - else "", + str(p.best_prediction.id) if p.best_prediction is not None else "", + p.best_prediction.snapped_point.wkt if p.best_prediction is not None else "" ] f.write(constants.COLUMN_SEPARATOR.join(columns) + "\n") - with open(output_file_name + ".auto_metrics.txt", "w") as f: + with open(output_file_name + ".auto_metrics.txt",'w') as f: header = [ "id", "source_length", @@ -259,7 +174,7 @@ def output_trace_snap_results( "revisited_via_points", "revisited_segments", "elapsed", - "source_wkt", + "source_wkt" ] f.write(constants.COLUMN_SEPARATOR.join(header) + "\n") for r in match_results: @@ -269,7 +184,7 @@ def output_trace_snap_results( str(r.route_length), str(len(r.points)), str(r.points_with_matches), - rf"{(100 * r.points_with_matches / len(r.points)):.2f}", + rf"{(100*r.points_with_matches/len(r.points)):.2f}", str(r.target_candidates_count), str(len(r.matched_target_ids)), str(r.avg_dist_to_road), @@ -281,51 +196,31 @@ def output_trace_snap_results( ] f.write(constants.COLUMN_SEPARATOR.join(columns) + "\n") - def set_best_path_predictions(points: Iterable[PointSnapInfo]): """Sets the best prediction for each point in the sequence, starting from the end and going backwards following the best_prev_prediction chain""" last_point = points[-1] - if ( - last_point.predictions is None - or len(last_point.predictions) == 0 - or last_point.predictions[0].best_log_prob == 0 - ): - return # no path found - - last_point.best_prediction = last_point.predictions[ - 0 - ] # this is sorted descending by probability, so the first one is the best - for idx in range(len(points) - 2, -1, -1): + if last_point.predictions is None or len(last_point.predictions) == 0 or last_point.predictions[0].best_log_prob == 0: + return # no path found + + last_point.best_prediction = last_point.predictions[0] # this is sorted descending by probability, so the first one is the best + for idx in range(len(points)-2, -1, -1): if points[idx + 1].best_prediction is not None: - points[idx].best_prediction = points[ - idx + 1 - ].best_prediction.best_prev_prediction + points[idx].best_prediction = points[idx + 1].best_prediction.best_prev_prediction else: - if not (points[idx].ignore) and len(points[idx].predictions) > 0: + if not(points[idx].ignore) and len(points[idx].predictions) > 0: points[idx].best_prediction = points[idx].predictions[0] - -def extend_sequence( - steps: Iterable[RouteStep], prev_prediction: SnappedPointPrediction -): +def extend_sequence(steps: Iterable[RouteStep], prev_prediction: SnappedPointPrediction): """Extends the sequence of the traveled segments up to the previous point with the new steps; also returns the number of revisited segments and via points""" revisited_via_points_count = 0 revisited_segments_count = 0 - extended_sequence = ( - prev_prediction.best_sequence.copy() - if prev_prediction.best_sequence is not None - else [] - ) + extended_sequence = prev_prediction.best_sequence.copy() if prev_prediction.best_sequence is not None else [] revisited_segments_count = 0 added_via_points = [] for step in steps: - if ( - len(extended_sequence) == 0 or step.feature.id != extended_sequence[-1] - ): # either first step or new feature - if ( - len(extended_sequence) > 0 and step.feature.id in extended_sequence - ): # different than prev segment but present in the sequence, so we are revisiting it + if len(extended_sequence) == 0 or step.feature.id != extended_sequence[-1]: # either first step or new feature + if len(extended_sequence) > 0 and step.feature.id in extended_sequence: # different than prev segment but present in the sequence, so we are revisiting it revisited_segments_count += 1 extended_sequence.append(step.feature.id) if step.via_point is not None: @@ -339,7 +234,7 @@ def extend_sequence( for vp in p.best_route_via_points: all_prev_via_points.add(vp) if len(all_prev_via_points) > 100: - break # optimization for very long traces, don't need to check all of them, just the recent ones + break # optimization for very long traces, don't need to check all of them, just the recent ones p = p.best_prev_prediction for added_via_point in added_via_points: @@ -347,26 +242,20 @@ def extend_sequence( revisited_via_points_count += 1 return (extended_sequence, revisited_segments_count, revisited_via_points_count) - -def get_trace_matches( - source_feature: MatchableFeature, - target_candidates: Iterable[MatchableFeature], - options: TraceSnapOptions, -) -> TraceMatchResult: +def get_trace_matches(source_feature: MatchableFeature, target_candidates: Iterable[MatchableFeature], options: TraceSnapOptions) -> TraceMatchResult: """Matches a `source_feature` trace to most likely traveled `targe_candidates` road segments""" start = timer() - feature_id_to_connected_features = get_feature_id_to_connected_features( - target_candidates - ) + feature_id_to_connected_features = get_feature_id_to_connected_features(target_candidates) filter_feature_ids = set(map(lambda x: x.id, target_candidates)) - times = source_feature.properties.get("times") + times = source_feature.properties.get('times') points = [] prev_point = None sequence_breaks = 0 for idx, coord in enumerate(source_feature.geometry.coords): + original_point = Point(coord[0], coord[1]) predictions = [] @@ -376,10 +265,7 @@ def get_trace_matches( if distance_to_road > options.max_point_to_road_distance: continue - emission_prob = ( - (1 / (math.sqrt(2 * math.pi) * options.sigma)) - * math.exp(-0.5 * ((distance_to_road / options.sigma) ** 2)) - ) # measurement probability - if was on this road how likely is it to have measured the point at this distance + emission_prob = (1 / (math.sqrt(2*math.pi) * options.sigma)) * math.exp(-0.5 * ((distance_to_road/options.sigma)**2)) # measurement probability - if was on this road how likely is it to have measured the point at this distance best_log_prob = None best_transition_prob = None best_prev_prediction = None @@ -395,67 +281,32 @@ def get_trace_matches( best_transition_prob = 1 best_sequence = [target_feature.id] else: - trace_dist_from_prev_point = get_distance( - original_point, prev_point.original_point - ) + trace_dist_from_prev_point = get_distance(original_point, prev_point.original_point) for prev_prediction in prev_point.predictions: - if ( - not (options.allow_loops) - and prev_prediction.best_sequence is not None - and target_feature.id in prev_prediction.best_sequence - and prev_prediction.referenced_feature.id != target_feature.id - ): + if not(options.allow_loops) and not(prev_prediction.best_sequence is None) and target_feature.id in prev_prediction.best_sequence and prev_prediction.referenced_feature.id != target_feature.id: # already part of best sequence, but then moved to a different segment, so this is not a good candidate, it means this would walk back on itself continue - route = get_shortest_route( - target_candidates, - feature_id_to_connected_features, - prev_prediction.referenced_feature, - target_feature, - prev_prediction.snapped_point, - snapped_point, - filter_feature_ids, - [] if options.allow_loops else prev_prediction.best_sequence, - ) + route = get_shortest_route(target_candidates, feature_id_to_connected_features, prev_prediction.referenced_feature, target_feature, prev_prediction.snapped_point, snapped_point, filter_feature_ids, [] if options.allow_loops else prev_prediction.best_sequence) # check distance is not float('inf') - if route is None or route.distance == float("inf"): + if route is None or route.distance == float('inf') : # couldn't find path, skip this prev_match as impossible to transition from it to this match continue dist_diff = abs(trace_dist_from_prev_point - route.distance) - transition_prob = (1 / options.beta) * math.exp( - -dist_diff / options.beta - ) - - ( - extended_sequence, - revisited_segments_count, - revisited_via_points_count, - ) = extend_sequence(route.steps, prev_prediction) - transition_prob *= math.exp( - -revisited_via_points_count - * options.revisit_via_point_penalty_weight - ) # todo: what's the right way to penalize revisiting via points? - transition_prob *= math.exp( - -revisited_segments_count - * options.revisit_segment_penalty_weight - ) # todo: what's the right way to penalize revisiting segments? - - if ( - dist_diff > options.max_route_to_trace_distance_difference - or transition_prob <= 0 - ): + transition_prob = (1 / options.beta) * math.exp(-dist_diff / options.beta) + + extended_sequence, revisited_segments_count, revisited_via_points_count = extend_sequence(route.steps, prev_prediction) + transition_prob *= math.exp(-revisited_via_points_count * options.revisit_via_point_penalty_weight) # todo: what's the right way to penalize revisiting via points? + transition_prob *= math.exp(-revisited_segments_count * options.revisit_segment_penalty_weight) # todo: what's the right way to penalize revisiting segments? + + if dist_diff > options.max_route_to_trace_distance_difference or transition_prob <= 0: continue - # match_prob = prev_prediction.best_prob * emission_prob * transition_prob + #match_prob = prev_prediction.best_prob * emission_prob * transition_prob # probabilities multiplied over many points go to zero (floating point underflow), so use log of product is sum of logs - match_log_prob = ( - prev_prediction.best_log_prob - + math.log(emission_prob) - + math.log(transition_prob) - ) - # print(f'point#{idx} prev_prediction={prev_prediction.id} transition_prob={transition_prob} emission_prob={emission_prob} match_prob={match_prob} route_dist_from_prev_point={route_dist_from_prev_point} trace_dist_from_prev_point={trace_dist_from_prev_point} dist_diff={dist_diff}') + match_log_prob = prev_prediction.best_log_prob + math.log(emission_prob) + math.log(transition_prob) + #print(f'point#{idx} prev_prediction={prev_prediction.id} transition_prob={transition_prob} emission_prob={emission_prob} match_prob={match_prob} route_dist_from_prev_point={route_dist_from_prev_point} trace_dist_from_prev_point={trace_dist_from_prev_point} dist_diff={dist_diff}') if best_log_prob is None or match_log_prob > best_log_prob: best_log_prob = match_log_prob best_transition_prob = transition_prob @@ -471,42 +322,20 @@ def get_trace_matches( # todo: also include the intermediate features in route.path if best_log_prob is None: - continue # couldn't find a path to this point, skip it - # print(f'point#{idx} candidate feature={target_feature.id} best_log_prob={best_log_prob} best_prev_point={best_prev_prediction.id if best_prev_prediction is not None else None} best_transition_prob={best_transition_prob} emission_prob={emission_prob} distance_to_road={distance_to_road}') - prediction = SnappedPointPrediction( - target_feature.id, - snapped_point, - target_feature, - distance_to_road, - best_route_dist_from_prev_point, - emission_prob, - best_transition_prob, - best_log_prob, - best_prev_prediction, - best_sequence, - best_route_via_points, - best_revisited_via_points_count, - best_revisited_segments_count, - ) + continue # couldn't find a path to this point, skip it + #print(f'point#{idx} candidate feature={target_feature.id} best_log_prob={best_log_prob} best_prev_point={best_prev_prediction.id if best_prev_prediction is not None else None} best_transition_prob={best_transition_prob} emission_prob={emission_prob} distance_to_road={distance_to_road}') + prediction = SnappedPointPrediction(target_feature.id, snapped_point, target_feature, distance_to_road, best_route_dist_from_prev_point, emission_prob, best_transition_prob, best_log_prob, best_prev_prediction, best_sequence, best_route_via_points, best_revisited_via_points_count, best_revisited_segments_count) predictions.append(prediction) predictions.sort(key=lambda x: x.best_log_prob, reverse=True) - time_since_prev_point = ( - None - if times is None or prev_point is None - else get_seconds_elapsed(times[prev_point.index], times[idx]) - ) + time_since_prev_point = None if times is None or prev_point is None else get_seconds_elapsed(times[prev_point.index], times[idx]) time = None if times is None else times[idx] - point = PointSnapInfo( - idx, original_point, time, time_since_prev_point, predictions - ) + point = PointSnapInfo(idx, original_point, time, time_since_prev_point, predictions) points.append(point) if len(predictions) > 0: - prev_point = ( - point # don't update prev_point unless it has at least one prediction - ) + prev_point = point # don't update prev_point unless it has at least one prediction else: # no predictions for this point, so ignore current point and previous point to attempt to recover sequence; # if gap between current point and prev_point is too big, abandon the prev_point and reset; @@ -517,16 +346,9 @@ def get_trace_matches( if prev_point.index > 0: prev_point = points[prev_point.index - 1] # gap with no candidates too big if 60seconds or 200m since last point - if ( - ( - time_since_prev_point is not None - and time_since_prev_point - > options.broken_time_gap_reset_sequence - ) - or trace_dist_from_prev_point - > options.broken_distance_gap_reset_sequence - ): - # print(rf"#{str(idx)}: sequence break; time_since_prev_point={time_since_prev_point} trace_dist_from_prev_point={trace_dist_from_prev_point}") + if (time_since_prev_point is not None and time_since_prev_point > options.broken_time_gap_reset_sequence) or \ + trace_dist_from_prev_point > options.broken_distance_gap_reset_sequence: + #print(rf"#{str(idx)}: sequence break; time_since_prev_point={time_since_prev_point} trace_dist_from_prev_point={trace_dist_from_prev_point}") # we have a sequence break, reset prev point, new sequence will start from next point sequence_breaks += 1 prev_point = None @@ -538,19 +360,10 @@ def get_trace_matches( end = timer() elapsed = end - start source_feature_length = get_linestring_length(source_feature.geometry) - t = TraceMatchResult( - source_feature.id, - source_feature.geometry.wkt, - points, - source_feature_length, - len(target_candidates), - elapsed=elapsed, - sequence_breaks=sequence_breaks, - ) + t = TraceMatchResult(source_feature.id, source_feature.geometry.wkt, points, source_feature_length, len(target_candidates), elapsed=elapsed, sequence_breaks=sequence_breaks) set_trace_match_metrics(t) return t - def set_trace_match_metrics(t: TraceMatchResult) -> None: matched_target_ids = set() route_length = 0 @@ -559,108 +372,56 @@ def set_trace_match_metrics(t: TraceMatchResult) -> None: revisited_segments = 0 points_with_matches = 0 for point in t.points: - if ( - point.best_prediction is not None - and point.best_prediction.referenced_feature is not None - ): + if point.best_prediction is not None and point.best_prediction.referenced_feature is not None: points_with_matches += 1 - route_length += ( - point.best_prediction.route_distance_to_prev_point - if point.best_prediction.route_distance_to_prev_point is not None - else 0 - ) + route_length += point.best_prediction.route_distance_to_prev_point if point.best_prediction.route_distance_to_prev_point is not None else 0 dist_to_road += point.best_prediction.distance_to_snapped_road - revisited_via_points += ( - point.best_prediction.best_revisited_via_points_count - ) + revisited_via_points += point.best_prediction.best_revisited_via_points_count revisited_segments += point.best_prediction.best_revisited_segments_count matched_target_ids.add(point.best_prediction.referenced_feature.id) t.matched_target_ids = list(matched_target_ids) t.points_with_matches = points_with_matches t.route_length = round(route_length, 2) - t.avg_dist_to_road = ( - round(dist_to_road / points_with_matches, 2) - if points_with_matches > 0 - else None - ) + t.avg_dist_to_road = round(dist_to_road / points_with_matches, 2) if points_with_matches > 0 else None t.revisited_via_points = revisited_via_points t.revisited_segments = revisited_segments - -def print_stats( - source_features: Iterable[MatchableFeature], - target_features: Iterable[MatchableFeature], - match_results: Iterable[TraceMatchResult], - total_elapsed: float, - avg_runtime_per_feature: float, -): +def print_stats(source_features: Iterable[MatchableFeature], target_features: Iterable[MatchableFeature], match_results: Iterable[TraceMatchResult], total_elapsed: float, avg_runtime_per_feature: float): num_traces = len(source_features) - total_route_length = sum([r.route_length for r in match_results]) / 1000 # in km - total_traces_length = sum([r.source_length for r in match_results]) / 1000 # in km + total_route_length = sum([r.route_length for r in match_results]) / 1000 # in km + total_traces_length = sum([r.source_length for r in match_results]) / 1000 # in km total_candidates = sum([r.target_candidates_count for r in match_results]) total_matches = sum([len(r.matched_target_ids) for r in match_results]) total_sequence_breaks = sum([r.sequence_breaks for r in match_results]) total_revisited_via_points = sum([r.revisited_via_points for r in match_results]) total_revisited_segments = sum([r.revisited_segments for r in match_results]) - total_traces_with_matches = sum( - [1 for r in match_results if r.points_with_matches > 0] - ) - total_avg_dist_to_road = sum( - [r.avg_dist_to_road for r in match_results if r.points_with_matches > 0] - ) - avg_runtime_per_km = ( - total_elapsed / total_traces_length if total_traces_length > 0 else None - ) - avg_dist_to_road = ( - round(total_avg_dist_to_road / total_traces_with_matches, 2) - if total_traces_with_matches > 0 - else None - ) + total_traces_with_matches = sum([1 for r in match_results if r.points_with_matches > 0]) + total_avg_dist_to_road = sum([r.avg_dist_to_road for r in match_results if r.points_with_matches > 0]) + avg_runtime_per_km = total_elapsed / total_traces_length if total_traces_length > 0 else None + avg_dist_to_road = round(total_avg_dist_to_road / total_traces_with_matches, 2) if total_traces_with_matches > 0 else None print("==================================================================") print("Totals:") print("==================================================================") print(rf"Traces.............................{num_traces}") print(rf"Target features....................{len(target_features)}") - print( - rf"Elapsed:...........................{round(total_elapsed // 60)}min {total_elapsed % 60:.3f}s" - ) + print(rf"Elapsed:...........................{round(total_elapsed//60)}min {total_elapsed%60:.3f}s") print(rf"Avg runtime/trace..................{avg_runtime_per_feature:.3f}s") print(rf"Avg runtime/km.....................{avg_runtime_per_km:.3f}s") print(rf"Avg distance to snapped road.......{avg_dist_to_road}m") print(rf"Snapped route length...............{total_route_length:.2f}km") print(rf"GPS traces length..................{total_traces_length:.2f}km") - print( - rf"Snapped route len/gps len..........{(total_route_length / total_traces_length):.2f}" - ) - print( - rf"Avg number of candidate segments...{(total_candidates / num_traces):.2f}/trace, {(total_candidates / total_traces_length):.2f}/km" - ) - print( - rf"Avg number of matched segments.....{(total_matches / num_traces):.2f}/trace, {(total_matches / total_traces_length):.2f}/km" - ) - print( - rf"Avg number of sequence breaks......{(total_sequence_breaks / num_traces):.2f}/trace, {(total_sequence_breaks / total_traces_length):.2f}/km" - ) - print( - rf"Avg number of revisited via points.{(total_revisited_via_points / num_traces):.2f}/trace, {(total_revisited_via_points / total_traces_length):.2f}/km" - ) - print( - rf"Avg number of revisited segments...{(total_revisited_segments / num_traces):.2f}/trace, {(total_revisited_segments / total_traces_length):.2f}/km" - ) + print(rf"Snapped route len/gps len..........{(total_route_length/total_traces_length):.2f}") + print(rf"Avg number of candidate segments...{(total_candidates/num_traces):.2f}/trace, {(total_candidates/total_traces_length):.2f}/km") + print(rf"Avg number of matched segments.....{(total_matches/num_traces):.2f}/trace, {(total_matches/total_traces_length):.2f}/km") + print(rf"Avg number of sequence breaks......{(total_sequence_breaks/num_traces):.2f}/trace, {(total_sequence_breaks/total_traces_length):.2f}/km") + print(rf"Avg number of revisited via points.{(total_revisited_via_points/num_traces):.2f}/trace, {(total_revisited_via_points/total_traces_length):.2f}/km") + print(rf"Avg number of revisited segments...{(total_revisited_segments/num_traces):.2f}/trace, {(total_revisited_segments/total_traces_length):.2f}/km") print("==================================================================") - -def snap_traces( - features_to_match_file: str, - overture_file: str, - output_file: str, - res: int, - snap_options: TraceSnapOptions = None, - output_for_judgment: bool = False, -) -> None: +def snap_traces(features_to_match_file: str, overture_file: str, output_file: str, res: int, snap_options: TraceSnapOptions=None, output_for_judgment: bool=False) -> None: if snap_options is None: - snap_options = TraceSnapOptions() # loads default options + snap_options = TraceSnapOptions() # loads default options # save the options we used next to the output file for debugging or comparison with other runs with open(output_file + ".options.json", "w") as f: @@ -669,21 +430,19 @@ def snap_traces( start = timer() print("Loading features...") to_match_prop_filter = {} - # to_match_prop_filter["id"] = "manual_trace#4" + #to_match_prop_filter["id"] = "manual_trace#4" to_match = load_matchable_set(features_to_match_file, is_multiline=False, res=res) features_to_match = to_match.features_by_id.values() if len(features_to_match) == 0: print("no features to match") exit() - overture = load_matchable_set( - overture_file, is_multiline=True, properties_filter={"type": "segment"}, res=res - ) - features_overture = overture.features_by_id.values() + overture = load_matchable_set(overture_file, is_multiline=True, properties_filter = {"type": "segment"}, res=res) + features_overture =overture.features_by_id.values() print("Features to match: " + str(len(features_to_match))) print("Features Overture: " + str(len(features_overture))) end = timer() - print(f"Loading time: {(end - start):.2f}s") + print(f"Loading time: {(end-start):.2f}s") i = 0 match_results = [] @@ -691,142 +450,46 @@ def snap_traces( for source_feature in features_to_match: i += 1 - target_candidates = get_features_with_cells( - overture.features_by_cell, to_match.cells_by_id[source_feature.id] - ) + target_candidates = get_features_with_cells(overture.features_by_cell, to_match.cells_by_id[source_feature.id]) match_res = get_trace_matches(source_feature, target_candidates, snap_options) match_results.append(match_res) total_elapsed += match_res.elapsed avg_runtime_per_feature = total_elapsed / i - if i % 1 == 0: - print( - rf"trace#{str(i)} length={match_res.source_length} route_length={round(match_res.route_length)} " - + rf"points={len(source_feature.geometry.coords)} points_w_matches={match_res.points_with_matches} " - + rf"candidates={match_res.target_candidates_count} matched target_ids: {str(len(match_res.matched_target_ids))} " - + rf"elapsed: {match_res.elapsed:.2f}s; avg runtime/feature: {avg_runtime_per_feature:.3f}s" - ) - - print_stats( - features_to_match, - features_overture, - match_results, - total_elapsed, - avg_runtime_per_feature, - ) + if i%1 == 0: + print(rf"trace#{str(i)} length={match_res.source_length} route_length={round(match_res.route_length)} " + \ + rf"points={len(source_feature.geometry.coords)} points_w_matches={match_res.points_with_matches} " + \ + rf"candidates={match_res.target_candidates_count} matched target_ids: {str(len(match_res.matched_target_ids))} " + \ + rf"elapsed: {match_res.elapsed:.2f}s; avg runtime/feature: {avg_runtime_per_feature:.3f}s") + + print_stats(features_to_match, features_overture, match_results, total_elapsed, avg_runtime_per_feature) print("Writing results...") start = timer() output_trace_snap_results(match_results, output_file, output_for_judgment) end = timer() - print(f"Writing time: {(end - start):.2f}s") - calculate_error_rate( - features_to_match_file.replace(".geojson", ".labeled.txt"), - overture.features_by_id, - match_results, - ) - + print(f"Writing time: {(end-start):.2f}s") + calculate_error_rate(features_to_match_file.replace('.geojson', '.labeled.txt'), overture.features_by_id, match_results) def get_args(): - parser = argparse.ArgumentParser( - description="", - add_help=True, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - ) - parser.add_argument( - "--input-to-match", - help="Input file containing features to match in geojson format", - required=True, - ) - parser.add_argument( - "--input-overture", - help="Input file containing overture features", - required=True, - ) - parser.add_argument( - "--output", help="Output file containing match results", required=True - ) - parser.add_argument( - "--resolution", - help="H3 cell resolution used to pre-filter candidates", - type=int, - default=constants.DEFAULT_H3_RESOLUTION, - choices=range(0, 15), - ) - parser.add_argument( - "--sigma", - type=float, - help="Sigma param - controlling tolerance to GPS noise", - required=False, - default=constants.DEFAULT_SIGMA, - ) - parser.add_argument( - "--beta", - type=float, - help="Beta param - controlling confidence in route", - required=False, - default=constants.DEFAULT_BETA, - ) - parser.add_argument( - "--allow_loops", - type=bool, - help="Allow same sequence to revisit same segment with other segment(s) in between", - required=False, - default=constants.DEFAULT_ALLOW_LOOPS, - ) - parser.add_argument( - "--max_point_to_road_distance", - type=float, - help="Maximum distance in meters between a trace point and a match candidate road", - required=False, - default=constants.DEFAULT_MAX_POINT_TO_ROAD_DISTANCE, - ) - parser.add_argument( - "--max_route_to_trace_distance_difference", - type=float, - help="Maximum difference between route and trace lengths in meters", - required=False, - default=constants.DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE, - ) - parser.add_argument( - "--revisit_segment_penalty_weight", - type=float, - help="How much to penalize a route with one segment revisit", - required=False, - default=constants.DEFAULT_SEGMENT_REVISIT_PENALTY, - ) - parser.add_argument( - "--revisit_via_point_penalty_weight", - type=float, - help="How much to penalize a route with one via-point revisit", - required=False, - default=constants.DEFAULT_VIA_POINT_PENALTY_WEIGHT, - ) - parser.add_argument( - "--broken_time_gap_reset_sequence", - type=float, - help="How big the time gap in seconds between points without valid route options before we consider it a broken sequence", - required=False, - default=constants.DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE, - ) - parser.add_argument( - "--broken_distance_gap_reset_sequence", - type=float, - help="How big the distance gap in meters between points without valid route options before we consider it a broken sequence", - required=False, - default=constants.DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE, - ) - parser.add_argument( - "--j", - action="store_true", - help="Also output the matches as a 'pre-labeled' file for judgment", - default=False, - required=False, - ) + parser = argparse.ArgumentParser(description="", add_help=True, formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("--input-to-match", help="Input file containing features to match in geojson format", required=True) + parser.add_argument("--input-overture", help="Input file containing overture features", required=True) + parser.add_argument("--output", help="Output file containing match results", required=True) + parser.add_argument("--resolution", help="H3 cell resolution used to pre-filter candidates", type=int, default=constants.DEFAULT_H3_RESOLUTION, choices=range(0,15)) + parser.add_argument("--sigma", type=float, help=f"Sigma param - controlling tolerance to GPS noise", required=False, default=constants.DEFAULT_SIGMA) + parser.add_argument("--beta", type=float, help=f"Beta param - controlling confidence in route", required=False, default=constants.DEFAULT_BETA) + parser.add_argument("--allow_loops", type=bool, help=f"Allow same sequence to revisit same segment with other segment(s) in between", required=False, default=constants.DEFAULT_ALLOW_LOOPS) + parser.add_argument("--max_point_to_road_distance", type=float, help=f"Maximum distance in meters between a trace point and a match candidate road", required=False, default=constants.DEFAULT_MAX_POINT_TO_ROAD_DISTANCE) + parser.add_argument("--max_route_to_trace_distance_difference", type=float, help=f"Maximum difference between route and trace lengths in meters", required=False, default=constants.DEFAULT_MAX_ROUTE_TO_TRACE_DISTANCE_DIFFERENCE) + parser.add_argument("--revisit_segment_penalty_weight", type=float, help="How much to penalize a route with one segment revisit", required=False, default=constants.DEFAULT_SEGMENT_REVISIT_PENALTY) + parser.add_argument("--revisit_via_point_penalty_weight", type=float, help="How much to penalize a route with one via-point revisit", required=False, default=constants.DEFAULT_VIA_POINT_PENALTY_WEIGHT) + parser.add_argument("--broken_time_gap_reset_sequence", type=float, help="How big the time gap in seconds between points without valid route options before we consider it a broken sequence", required=False, default=constants.DEFAULT_BROKEN_TIME_GAP_RESET_SEQUENCE) + parser.add_argument("--broken_distance_gap_reset_sequence", type=float, help="How big the distance gap in meters between points without valid route options before we consider it a broken sequence", required=False, default=constants.DEFAULT_BROKEN_DISTANCE_GAP_RESET_SEQUENCE) + parser.add_argument("--j", action="store_true", help="Also output the matches as a 'pre-labeled' file for judgment", default=False, required=False) return parser.parse_args() - def get_trace_snap_options_from_args(args): return TraceSnapOptions( sigma=args.sigma, @@ -837,18 +500,9 @@ def get_trace_snap_options_from_args(args): revisit_segment_penalty_weight=args.revisit_segment_penalty_weight, revisit_via_point_penalty_weight=args.revisit_via_point_penalty_weight, broken_time_gap_reset_sequence=args.broken_time_gap_reset_sequence, - broken_distance_gap_reset_sequence=args.broken_distance_gap_reset_sequence, - ) - + broken_distance_gap_reset_sequence=args.broken_distance_gap_reset_sequence) if __name__ == "__main__": args = get_args() trace_snap_options = get_trace_snap_options_from_args(args) - snap_traces( - args.input_to_match, - args.input_overture, - args.output, - args.resolution, - trace_snap_options, - output_for_judgment=args.j, - ) + snap_traces(args.input_to_match, args.input_overture, args.output, args.resolution, trace_snap_options, output_for_judgment=args.j) diff --git a/gers/examples/python/route_utils.py b/gers/examples/python/route_utils.py index ea497b8a0..51cbe71bc 100644 --- a/gers/examples/python/route_utils.py +++ b/gers/examples/python/route_utils.py @@ -1,20 +1,10 @@ -from collections.abc import Iterable - -from match_classes import MatchableFeature, Route, RouteStep -from shapely.geometry import Point -from shapely.ops import nearest_points from utils import get_distance +from shapely.ops import nearest_points +from match_classes import RouteStep, Route, MatchableFeature +from shapely.geometry import Point +from typing import Dict, Tuple, Iterable - -def get_route_step_dist( - feat_before_from: MatchableFeature, - feat_from: MatchableFeature, - feat_to: MatchableFeature, - start_feature: MatchableFeature, - end_feature: MatchableFeature, - start_point: Point, - end_point: Point, -) -> tuple[Point, float]: +def get_route_step_dist(feat_before_from: MatchableFeature, feat_from: MatchableFeature, feat_to: MatchableFeature, start_feature: MatchableFeature, end_feature: MatchableFeature, start_point: Point, end_point: Point) -> Tuple[Point, float]: """get distance traveled on one feature `feat_from` having entering from `feat_before_from` and exiting to `feat_to`, given that the whole route starts at `start_feature` and ends at `end_feature`""" # todo: this a distance approximation for now as length of straight line from entry point to exit point on the feat_from feature, but works reasonably well for the data seen so far feat_from_exit_point, p2 = nearest_points(feat_from.geometry, feat_to.geometry) @@ -23,11 +13,9 @@ def get_route_step_dist( if feat_from.id == start_feature.id: d += get_distance(start_point, feat_from_exit_point) else: - p0_before, feat_from_entry_point = nearest_points( - feat_before_from.geometry, feat_from.geometry - ) + p0_before, feat_from_entry_point = nearest_points(feat_before_from.geometry, feat_from.geometry) d += get_distance(feat_from_entry_point, feat_from_exit_point) - + if feat_to.id == end_feature.id: d += get_distance(end_point, p2) # else there is no distance to add @@ -35,21 +23,11 @@ def get_route_step_dist( # todo: add basic penalties like allowed travel direction disagreement, road class change cost, etc. return feat_from_exit_point, d - -def get_shortest_route( - features: Iterable[MatchableFeature], - feature_id_to_connected_features: dict[str, Iterable[MatchableFeature]], - start_feature: MatchableFeature, - end_feature: MatchableFeature, - start_point: Point, - end_point: Point, - allowed_ids: Iterable[str], - blocked_ids: Iterable[str], -) -> Route: +def get_shortest_route(features: Iterable[MatchableFeature], feature_id_to_connected_features: Dict[str, Iterable[MatchableFeature]], start_feature: MatchableFeature, end_feature: MatchableFeature, start_point: Point, end_point: Point, allowed_ids: Iterable[str], blocked_ids: Iterable[str]) -> Route: """ Dijsktra's algorithm to find shortest route between start and end features. Remember for each traveled feature the entry via_point. """ - + # start and end are same feature, no route calculation needed, just distance if start_feature.id == end_feature.id: dist = get_distance(start_point, end_point) @@ -64,7 +42,7 @@ def get_shortest_route( for f in features: if f.id in blocked_ids and f.id != start_feature.id: continue - dist[f.id] = float("inf") + dist[f.id] = float('inf') prev[f.id] = None prev_via_point[f.id] = None feats_to_visit.append(f) @@ -73,55 +51,41 @@ def get_shortest_route( while len(feats_to_visit) > 0: current_feature = feats_to_visit[0] - min_dist = float("inf") + min_dist = float('inf') for f in feats_to_visit: if dist[f.id] < min_dist: min_dist = dist[f.id] current_feature = f - if min_dist == float("inf"): - break # no more allowed connected features to visit + if min_dist == float('inf'): + break # no more allowed connected features to visit if current_feature.id == end_feature.id: - break # done, visited end_feature, don't need to calculate shortest path to all features + break # done, visited end_feature, don't need to calculate shortest path to all features feats_to_visit.remove(current_feature) ids_to_visit.remove(current_feature.id) connected_features = feature_id_to_connected_features[current_feature.id] for v in connected_features: - if ( - v.id not in allowed_ids - or (v.id in blocked_ids) - or v.id not in ids_to_visit - ): + if not(v.id in allowed_ids) or (v.id in blocked_ids) or not(v.id in ids_to_visit): continue - if v.id not in ids_to_visit: - continue # have already visited this feature + if not(v.id in ids_to_visit): + continue # have already visited this feature - via_point, d = get_route_step_dist( - prev[current_feature.id], - current_feature, - v, - start_feature, - end_feature, - start_point, - end_point, - ) + via_point, d = get_route_step_dist(prev[current_feature.id], current_feature, v, start_feature, end_feature, start_point, end_point) alternate_dist = dist[current_feature.id] + d if alternate_dist < dist[v.id]: dist[v.id] = alternate_dist prev[v.id] = current_feature prev_via_point[v.id] = via_point - + steps = [] current_feature = end_feature if prev[current_feature.id] is not None or current_feature.id == start_feature.id: while current_feature is not None: - steps.insert( - 0, RouteStep(current_feature, prev_via_point[current_feature.id]) - ) + steps.insert(0, RouteStep(current_feature, prev_via_point[current_feature.id])) current_feature = prev[current_feature.id] r = Route(round(dist[end_feature.id], 2), steps) - return r + return r \ No newline at end of file diff --git a/gers/examples/python/tests/match_traces_test.py b/gers/examples/python/tests/match_traces_test.py index 90a1b6b3f..07cd77a8b 100644 --- a/gers/examples/python/tests/match_traces_test.py +++ b/gers/examples/python/tests/match_traces_test.py @@ -1,26 +1,20 @@ -import json +import test_setup import os +import json import unittest - import constants from match_classes import TraceSnapOptions from match_traces import get_trace_matches -from utils import get_features_with_cells, load_matchable_set - +from utils import load_matchable_set, get_features_with_cells class TestTraces(unittest.TestCase): + def test_match_traces(self): - features_to_match_file = os.path.join( - constants.DATA_DIR, "macon-manual-traces.geojson" - ) - overture_file = os.path.join( - constants.DATA_DIR, "overture-transportation-macon.geojson" - ) + features_to_match_file = os.path.join(constants.DATA_DIR, "macon-manual-traces.geojson") + overture_file = os.path.join(constants.DATA_DIR, "overture-transportation-macon.geojson") res = 12 - to_match = load_matchable_set( - features_to_match_file, is_multiline=False, res=res - ) + to_match = load_matchable_set(features_to_match_file, is_multiline=False, res=res) self.assertIsNotNone(to_match) self.assertEqual(len(to_match.features_by_id), 4) @@ -28,26 +22,19 @@ def test_match_traces(self): self.assertIn(id_to_match, to_match.features_by_id) source_feature = to_match.features_by_id[id_to_match] - overture = load_matchable_set( - overture_file, - is_multiline=True, - properties_filter={"type": "segment"}, - res=res, - ) + overture = load_matchable_set(overture_file, is_multiline=True, properties_filter = {"type": "segment"}, res=res) self.assertIsNotNone(overture.features_by_id) self.assertGreater(len(overture.features_by_id), 20000) options = TraceSnapOptions(max_point_to_road_distance=30) - target_candidates = get_features_with_cells( - overture.features_by_cell, to_match.cells_by_id[source_feature.id] - ) + target_candidates = get_features_with_cells(overture.features_by_cell, to_match.cells_by_id[source_feature.id]) match_res = get_trace_matches(source_feature, target_candidates, options) self.assertIsNotNone(match_res) self.assertIsNotNone(match_res.points) self.assertEqual(len(match_res.points), len(source_feature.geometry.coords)) self.assertGreater(match_res.source_length, 5000) - self.assertGreater(match_res.route_length, 5000) + self.assertGreater(match_res.route_length, 5000) json_res = match_res.to_json() self.assertIsNotNone(json_res) @@ -61,6 +48,5 @@ def test_match_traces(self): if idx > 0: self.assertGreater(bp.route_distance_to_prev_point, 0.0) - -if __name__ == "__main__": - unittest.main() +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/gers/examples/python/tests/test_setup.py b/gers/examples/python/tests/test_setup.py index 2bf5cc41a..457368324 100644 --- a/gers/examples/python/tests/test_setup.py +++ b/gers/examples/python/tests/test_setup.py @@ -1,5 +1,5 @@ -import os import sys +import os parent_dir = os.path.dirname(os.path.abspath(__file__)) -sys.path.insert(0, os.path.dirname(parent_dir)) +sys.path.insert(0, os.path.dirname(parent_dir)) \ No newline at end of file diff --git a/gers/examples/python/tests/utils_test.py b/gers/examples/python/tests/utils_test.py index 6733e4999..22e82586b 100644 --- a/gers/examples/python/tests/utils_test.py +++ b/gers/examples/python/tests/utils_test.py @@ -1,21 +1,14 @@ +import test_setup import os import unittest - import constants -from shapely import LineString, Point -from utils import ( - get_distance, - get_intersecting_h3_cells_for_geo_json, - get_linestring_length, - load_matchable_set, -) - +from utils import get_distance, get_linestring_length, get_intersecting_h3_cells_for_geo_json, load_matchable_set +from shapely import Point, LineString class TestUtils(unittest.TestCase): + def test_load_matchable_set_geojson(self): - features_to_match_file = os.path.join( - constants.DATA_DIR, "macon-manual-traces.geojson" - ) + features_to_match_file = os.path.join(constants.DATA_DIR, "macon-manual-traces.geojson") s = load_matchable_set(features_to_match_file, res=12, is_multiline=False) self.assertIsNotNone(s) self.assertEqual(len(s.features_by_id), 4) @@ -25,60 +18,35 @@ def test_load_matchable_set_geojson(self): def test_get_distance(self): p1 = Point(-83.6878343, 32.8413587) p2 = Point(-83.6877941, 32.8413903) - + d = get_distance(p1, p2) self.assertAlmostEqual(d, 5.1, delta=0.1) def test_get_linestring_length(self): l = LineString([(-83.6878343, 32.8413587), (-83.6877941, 32.8413903)]) - + d = get_linestring_length(l) - self.assertAlmostEqual(d, 5.1, delta=0.1) + self.assertAlmostEqual(d, 5.1, delta=0.1) def test_get_intersecting_h3_cells_for_geo_json(self): - point = {"type": "Point", "coordinates": [-83.6197063, 32.8589311]} + point = { "type": "Point", "coordinates": [-83.6197063, 32.8589311] } actual_cells = get_intersecting_h3_cells_for_geo_json(point, 10) expected_cells = ["8a44c0a32867fff"] self.assertCountEqual(actual_cells, expected_cells) - line = { - "type": "LineString", - "coordinates": [ - [-83.61940200000001, 32.858034], - [-83.61940200000001, 32.859538], - ], - } + line = { "type": "LineString", "coordinates": [[-83.61940200000001, 32.858034], [-83.61940200000001, 32.859538]] } actual_cells = get_intersecting_h3_cells_for_geo_json(line, 10) expected_cells = ["8a44c0a3295ffff", "8a44c0a32867fff"] self.assertCountEqual(actual_cells, expected_cells) - polygon = { - "type": "Polygon", - "coordinates": [ - [ - [-83.6195695, 32.8591587], - [-83.6192584, 32.8583723], - [-83.619135, 32.8590437], - [-83.6195695, 32.8591587], - ] - ], - } + polygon = { "type": "Polygon", "coordinates": [[[-83.6195695, 32.8591587], [-83.6192584, 32.8583723], [-83.619135, 32.8590437], [-83.6195695, 32.8591587]]] } actual_cells = get_intersecting_h3_cells_for_geo_json(polygon, 10) expected_cells = ["8a44c0a32877fff", "8a44c0a32867fff", "8a44c0a3295ffff"] self.assertCountEqual(actual_cells, expected_cells) - ml = { - "type": "MultiLineString", - "coordinates": [ - [[-83.61940200000001, 32.858034], [-83.61940200000001, 32.859538]], - [[-83.6215878, 32.8580366], [-83.6202145, 32.8580546]], - ], - } + ml = { "type": "MultiLineString", "coordinates": [[[-83.61940200000001, 32.858034], [-83.61940200000001, 32.859538]], [[-83.6215878, 32.8580366], [-83.6202145, 32.8580546]]] } actual_cells = get_intersecting_h3_cells_for_geo_json(ml, 10) - expected_cells = [ - "8a44c0a3294ffff", - "8a44c0a32867fff", - "8a44c0a304b7fff", - "8a44c0a3295ffff", - ] + expected_cells = ["8a44c0a3294ffff", "8a44c0a32867fff", "8a44c0a304b7fff", "8a44c0a3295ffff"] self.assertCountEqual(actual_cells, expected_cells) + + diff --git a/gers/examples/python/utils.py b/gers/examples/python/utils.py index 7d613b19a..c66599f24 100644 --- a/gers/examples/python/utils.py +++ b/gers/examples/python/utils.py @@ -1,142 +1,116 @@ import csv import json -from collections.abc import Iterable -from typing import Any - -from dateutil import parser -from h3 import h3 -from haversine import Unit, haversine -from match_classes import MatchableFeature, MatchableFeaturesSet - -# from shapely.ops import transform +from haversine import haversine, Unit +#from shapely.ops import transform from shapely import wkt -from shapely.geometry import mapping, shape +from shapely.geometry import shape, mapping from shapely.geometry.base import BaseGeometry +from dateutil import parser +from typing import Any, Dict, Iterable +from h3 import h3 -# from pyproj import Geod - +from match_classes import MatchableFeature, MatchableFeaturesSet +#from pyproj import Geod def get_seconds_elapsed(t1_str, t2_str): t1 = parser.parse(t1_str) t2 = parser.parse(t2_str) return (t2 - t1).total_seconds() - def get_linestring_length(ls): length = 0 for i in range(len(ls.coords) - 1): lon1, lat1 = ls.coords[i] - lon2, lat2 = ls.coords[i + 1] - # _, _, d = geod.inv(lon1, lat1, lon2, lat2) + lon2, lat2 = ls.coords[i+1] + #_, _, d = geod.inv(lon1, lat1, lon2, lat2) d = haversine((lat1, lon1), (lat2, lon2), unit=Unit.METERS) - length += d + length += d return round(length, 2) - def get_distance(point1, point2): - # _, _, d = geod.inv(point1.x, point1.y, point2.x, point2.y) + #_, _, d = geod.inv(point1.x, point1.y, point2.x, point2.y) d = haversine((point1.y, point1.x), (point2.y, point2.x), unit=Unit.METERS) return round(d, 2) - def get_intersecting_h3_cells_for_line(coords, res): """for coordinates of a linestring, gets all h3 cells of given resolution that intersect the line""" - cells = set() - prevCell = None + cells = set() + prevCell = None for coord in coords: cell = h3.geo_to_h3(coord[1], coord[0], res) cells.add(cell) - if prevCell is None: + if (prevCell is None): prevCell = cell else: - if prevCell != cell: + if (prevCell != cell): # two consecutive coordinates in the linestring may be more than one cell apart # need to find intermediate cells between previous cell and the current one - if not h3.h3_indexes_are_neighbors(prevCell, cell): + if (not h3.h3_indexes_are_neighbors(prevCell, cell)): intermediateCells = h3.h3_line(prevCell, cell) for intermediateCell in intermediateCells: cells.add(intermediateCell) prevCell = cell return cells - -def get_intersecting_h3_cells_for_geo_json(geometry: Any, res: int) -> Iterable[str]: +def get_intersecting_h3_cells_for_geo_json(geometry: Any, res:int) -> Iterable[str]: """gets all h3 cells of given resolution that intersect the geometry.""" # h3 api wants two floats for point, geojson dict for polygon and custom code is needed for line and multi* geometries geojson = mapping(geometry) if isinstance(geometry, BaseGeometry) else geometry geom_type = geojson["type"] coords = geojson["coordinates"] - if geom_type.startswith("Multi"): + if (geom_type.startswith("Multi")): sub_geom_type = geom_type.replace("Multi", "") - sub_geoms = [ - {"type": sub_geom_type, "coordinates": sub_geom_coords} - for sub_geom_coords in coords - ] - sub_cells = [ - sub_cell - for sub_geom in sub_geoms - for sub_cell in get_intersecting_h3_cells_for_geo_json(sub_geom, res) - ] + sub_geoms = [{"type": sub_geom_type, "coordinates": sub_geom_coords } for sub_geom_coords in coords] + sub_cells = [sub_cell for sub_geom in sub_geoms for sub_cell in get_intersecting_h3_cells_for_geo_json(sub_geom, res)] return set(sub_cells) - if geom_type == "Point": + if (geom_type == "Point"): return set([h3.geo_to_h3(coords[1], coords[0], res)]) - if geom_type == "LineString": + if (geom_type == "LineString"): return get_intersecting_h3_cells_for_line(coords, res) - if geom_type == "Polygon": - innerCells = h3.polyfill( - geojson, res, True - ) # this only covers the tiles whose centers are inside the polygon + if (geom_type == "Polygon"): + innerCells = h3.polyfill(geojson, res, True) # this only covers the tiles whose centers are inside the polygon boundaryCells = get_intersecting_h3_cells_for_line(coords[0], res) return innerCells | boundaryCells - -def matches_properties_filter( - feature: dict[str, Any], properties_filter: dict[str, Any] -) -> bool: +def matches_properties_filter(feature: Dict[str, Any], properties_filter: Dict[str, Any]) -> bool: if properties_filter is None: return True feat_props = feature.get("properties") for prop in properties_filter: if prop == "id": return feature.get("id") == properties_filter[prop] - - if prop not in feat_props or ( - properties_filter[prop] != "*" - and feat_props[prop] != properties_filter[prop] - ): + + if not prop in feat_props or (properties_filter[prop] != "*" and feat_props[prop] != properties_filter[prop]): return False - return True - + return True -def get_matchable_feature(feature_dict: dict[str, Any]) -> MatchableFeature: +def get_matchable_feature(feature_dict: Dict[str, Any]) -> MatchableFeature: """creates a MatchableFeature from a dict with expected keys [id, geometry, properties], which could be either a geojson or parsed from a csv file with wkt geometry""" id = feature_dict.get("id") - geom = feature_dict.get("geometry") + geom = feature_dict.get("geometry") if type(geom) is dict and "type" in geom and "coordinates" in geom: # if it"s a geojson feature - s = shape(geom) - elif isinstance(geom, str): + s = shape(geom) + elif isinstance(geom, str): # if it"s a wkt string s = wkt.loads(geom) - props = feature_dict.get("properties") + props = feature_dict.get("properties") return MatchableFeature(id, s, props) - -def get_feature_cells(geom: Any, res: int, k_rings_to_add: int = 1): +def get_feature_cells(geom: Any, res: int, k_rings_to_add:int=1): """gets all h3 cells of given resolution that intersect the geometry, and also the cells that are k rings around the intersecting cells""" h3_cells = get_intersecting_h3_cells_for_geo_json(geom, res) if k_rings_to_add == 0: return list(h3_cells) - + rings = [h3.k_ring(h, k_rings_to_add) for h in h3_cells] return list(set(cell for r in rings for cell in r)) - -def parse_geojson(filename: str, is_multiline: bool) -> Iterable[dict[str, Any]]: - with open(filename, errors="ignore") as file: +def parse_geojson(filename: str, is_multiline: bool) -> Iterable[Dict[str, Any]]: + with open(filename, mode="r", errors="ignore") as file: if is_multiline: # text file with one geojson per line - i = 0 + i=0 features = [] for line in file: i += 1 @@ -144,22 +118,16 @@ def parse_geojson(filename: str, is_multiline: bool) -> Iterable[dict[str, Any]] geojson = json.loads(line.strip().rstrip(",")) features.append(geojson) except Exception as x: - print(rf"Line {i}: " + str(x)) + print(fr"Line {i}: " + str(x)) return features else: full_gj = json.loads(file.read()) - if full_gj.get("type") == "FeatureCollection": + if full_gj.get("type") == "FeatureCollection": return full_gj.get("features") else: return [full_gj] - -def get_matchable_set( - features: Iterable[dict[str, Any]], - properties_filter: dict = None, - res: int = 12, - limit_feature_count=-1, -) -> MatchableFeaturesSet: +def get_matchable_set(features: Iterable[Dict[str, Any]], properties_filter: dict=None, res: int=12, limit_feature_count=-1) -> MatchableFeaturesSet: features_by_id = {} cells_by_id = {} features_by_cell = {} @@ -172,7 +140,7 @@ def get_matchable_set( features_by_id[feature.id] = feature cells_by_id[feature.id] = get_feature_cells(feature.geometry, res) for cell in cells_by_id[feature.id]: - if cell not in features_by_cell: + if not cell in features_by_cell: features_by_cell[cell] = [] features_by_cell[cell].append(feature) except Exception as x: @@ -182,12 +150,11 @@ def get_matchable_set( break return MatchableFeaturesSet(features_by_id, cells_by_id, features_by_cell) - -def parse_csv(filename: str, delimiter: str = ",") -> MatchableFeaturesSet: +def parse_csv(filename: str, delimiter: str=",") -> MatchableFeaturesSet: features = [] - i = 0 - with open(filename, errors="ignore") as file: - reader = csv.DictReader(file, delimiter=delimiter) + i=0 + with open(filename, mode="r", errors="ignore") as file: + reader = csv.DictReader(file, delimiter=delimiter) for row in reader: feat_dict = {} feat_dict["properties"] = {} @@ -195,43 +162,28 @@ def parse_csv(filename: str, delimiter: str = ",") -> MatchableFeaturesSet: default_id = str(i) i += 1 key = k.lower() - if "id" not in feat_dict and "id" in key: + if not "id" in feat_dict and "id" in key: feat_dict["id"] = v - elif "geometry" not in feat_dict and ( - "geometry" in key or "wkt" in key - ): + elif not "geometry" in feat_dict and ("geometry" in key or "wkt" in key): feat_dict["geometry"] = v else: feat_dict["properties"][k] = v - if "id" not in feat_dict: + if not "id" in feat_dict: feat_dict["id"] = default_id - if "geometry" not in feat_dict: - if ( - "lat" in feat_dict["properties"] - and "lon" in feat_dict["properties"] - ): - feat_dict["geometry"] = ( - f"POINT({feat_dict['properties']['lon']} {feat_dict['properties']['lat']})" - ) + if not "geometry" in feat_dict: + if "lat" in feat_dict["properties"] and "lon" in feat_dict["properties"]: + feat_dict["geometry"] = f'POINT({feat_dict["properties"]["lon"]} {feat_dict["properties"]["lat"]})' else: continue - + features.append(feat_dict) return features - -def load_matchable_set( - filename: str, - properties_filter: dict = None, - res: int = 12, - limit_feature_count=-1, - is_multiline: bool = False, - delimiter: str = ",", -) -> MatchableFeaturesSet: +def load_matchable_set(filename: str, properties_filter: dict=None, res: int=12, limit_feature_count=-1, is_multiline: bool=False, delimiter: str=",") -> MatchableFeaturesSet: """loads a MatchableFeaturesSet from a geojson or csv file""" - extension = filename.split(".")[-1] + extension = filename.split(".")[-1] match extension: case "geojson" | "json": features = parse_geojson(filename, is_multiline=is_multiline) @@ -239,26 +191,22 @@ def load_matchable_set( features = parse_csv(filename, delimiter=delimiter) case _: raise Exception(f"Unsupported file type: {extension}") - + s = get_matchable_set(features, properties_filter, res, limit_feature_count) return s - - -def get_features_with_cells( - features_by_cell: dict[str, Iterable[MatchableFeature]], cells_filter: Iterable[str] -) -> Iterable[MatchableFeature]: + +def get_features_with_cells(features_by_cell: Dict[str, Iterable[MatchableFeature]], cells_filter: Iterable[str]) -> Iterable[MatchableFeature]: """gets all features in `features_by_cell` that intersect any of the cells in `cells_filter`""" with_cells = [] candidate_ids = set() for cell in cells_filter: if cell in features_by_cell: for candidate in features_by_cell[cell]: - if candidate.id not in candidate_ids: + if not candidate.id in candidate_ids: candidate_ids.add(candidate.id) with_cells.append(candidate) return with_cells - def write_json(results_json: Any, output_file_name: str): with open(output_file_name, "w") as f: - json.dump(results_json, f, indent=4) + json.dump(results_json, f, indent=4) \ No newline at end of file From 66a6c6509de4cf3a16c53a27892898b5a0178555 Mon Sep 17 00:00:00 2001 From: schapper Date: Fri, 3 Oct 2025 13:12:29 -0700 Subject: [PATCH 19/20] wip - PR comment on 397 - fix `sed` pattern https://github.com/OvertureMaps/schema/pull/397#discussion_r2402730438 The proximate issue was that BSD `sed` on MacOS doesn't support alternation unless you use `-E`. But also the alternation wasn't needed, so I removed it. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1454a0c06..ffd10bad9 100644 --- a/Makefile +++ b/Makefile @@ -33,9 +33,9 @@ mypy: uv-sync @# $$ escapes $ for make - sed needs literal $ for end-of-line anchor @find packages -maxdepth 1 -type d -name "overture-schema*" \ | sort \ - | sed 's:-theme$$::' \ + | sed 's|-theme$$||' \ | tr - . \ - | sed 's:^packages/\|:-p :' \ + | sed 's|^packages/|-p |' \ | xargs uv run mypy --no-error-summary @uv run mypy --no-error-summary packages/*/tests/*.py From f202c57a9c2662d5419385142e11e1b71abe7659 Mon Sep 17 00:00:00 2001 From: schapper Date: Fri, 3 Oct 2025 13:51:33 -0700 Subject: [PATCH 20/20] wip - cherrypick back the changes from #394, eeeep --- Makefile | 2 +- packages/overture-schema-sources/README.md | 23 ++ .../src/overture/__init__.py | 1 + .../src/overture/schema/__about__.py | 1 + .../src/overture/schema/__init__.py | 1 + .../src/overture/schema/py.typed | 0 .../src/overture/schema/sources/__init__.py | 7 + .../src/overture/schema/sources/__main__.py | 122 +++++++ .../src/overture/schema/sources/enums.py | 15 + .../src/overture/schema/sources/models.py | 180 +++++++++++ .../src/overture/schema/sources/types.py | 11 + .../tests/sources_baseline_schema.json | 297 ++++++++++++++++++ .../test_sources_json_schema_baseline.py | 28 ++ .../overture-schema-system/pyproject.toml | 4 +- .../src/overture/schema/system/__init__.py | 4 +- reference/examples/sources/basic-sources.yaml | 70 +++++ reference/examples/sources/minimal.yaml | 17 + uv.lock | 123 ++------ 18 files changed, 809 insertions(+), 97 deletions(-) create mode 100644 packages/overture-schema-sources/README.md create mode 100644 packages/overture-schema-sources/src/overture/__init__.py create mode 100644 packages/overture-schema-sources/src/overture/schema/__about__.py create mode 100644 packages/overture-schema-sources/src/overture/schema/__init__.py create mode 100644 packages/overture-schema-sources/src/overture/schema/py.typed create mode 100644 packages/overture-schema-sources/src/overture/schema/sources/__init__.py create mode 100644 packages/overture-schema-sources/src/overture/schema/sources/__main__.py create mode 100644 packages/overture-schema-sources/src/overture/schema/sources/enums.py create mode 100644 packages/overture-schema-sources/src/overture/schema/sources/models.py create mode 100644 packages/overture-schema-sources/src/overture/schema/sources/types.py create mode 100644 packages/overture-schema-sources/tests/sources_baseline_schema.json create mode 100644 packages/overture-schema-sources/tests/test_sources_json_schema_baseline.py create mode 100644 reference/examples/sources/basic-sources.yaml create mode 100644 reference/examples/sources/minimal.yaml diff --git a/Makefile b/Makefile index ffd10bad9..4060c9d88 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: default uv-sync check test-all test mypy reset-baseline-schemas +.PHONY: default uv-sync check test-all test docformat doctest mypy reset-baseline-schemas default: test-all diff --git a/packages/overture-schema-sources/README.md b/packages/overture-schema-sources/README.md new file mode 100644 index 000000000..2000ea7ad --- /dev/null +++ b/packages/overture-schema-sources/README.md @@ -0,0 +1,23 @@ +# Overture Schema Sources + +Shared models and validation tools for the data sources catalog used across +Overture Maps themes. + +## Contents + +- `overture.schema.sources` module, exporting the `Sources` aggregate model + and related dataset structures. +- CLI helper (`python -m overture.schema.sources`) to validate sources JSON + files against the schema. +- Reference examples and counterexamples under `schema/reference` illustrating + valid and invalid source structures. + +## Usage + +```bash +uv run python -m overture.schema.sources path/to/sources.json +``` + +This validates the payload and reports schema violations with dataset-level +context. The package also registers an `overture.models` entry point for the +`Sources` model so tools can discover the schema automatically. \ No newline at end of file diff --git a/packages/overture-schema-sources/src/overture/__init__.py b/packages/overture-schema-sources/src/overture/__init__.py new file mode 100644 index 000000000..8db66d3d0 --- /dev/null +++ b/packages/overture-schema-sources/src/overture/__init__.py @@ -0,0 +1 @@ +__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/packages/overture-schema-sources/src/overture/schema/__about__.py b/packages/overture-schema-sources/src/overture/schema/__about__.py new file mode 100644 index 000000000..3dc1f76bc --- /dev/null +++ b/packages/overture-schema-sources/src/overture/schema/__about__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/packages/overture-schema-sources/src/overture/schema/__init__.py b/packages/overture-schema-sources/src/overture/schema/__init__.py new file mode 100644 index 000000000..8db66d3d0 --- /dev/null +++ b/packages/overture-schema-sources/src/overture/schema/__init__.py @@ -0,0 +1 @@ +__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/packages/overture-schema-sources/src/overture/schema/py.typed b/packages/overture-schema-sources/src/overture/schema/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/packages/overture-schema-sources/src/overture/schema/sources/__init__.py b/packages/overture-schema-sources/src/overture/schema/sources/__init__.py new file mode 100644 index 000000000..8dddb62b3 --- /dev/null +++ b/packages/overture-schema-sources/src/overture/schema/sources/__init__.py @@ -0,0 +1,7 @@ +"""Sources schema package.""" + +__path__ = __import__("pkgutil").extend_path(__path__, __name__) + +from .models import Sources + +__all__ = ["Sources"] diff --git a/packages/overture-schema-sources/src/overture/schema/sources/__main__.py b/packages/overture-schema-sources/src/overture/schema/sources/__main__.py new file mode 100644 index 000000000..952869abe --- /dev/null +++ b/packages/overture-schema-sources/src/overture/schema/sources/__main__.py @@ -0,0 +1,122 @@ +"""CLI module for validating Sources JSON files. + +Usage: + uv run python -m overture.schema.sources +""" + +import json +import sys +from pathlib import Path + +from pydantic import ValidationError + +from .models import Sources + + +def main() -> None: + """Main function for CLI validation.""" + if len(sys.argv) != 2: + print( + "Usage: uv run python -m overture.schema.sources ", + file=sys.stderr, + ) + sys.exit(1) + + json_file_path = Path(sys.argv[1]) + + if not json_file_path.exists(): + print(f"Error: File '{json_file_path}' does not exist.", file=sys.stderr) + sys.exit(1) + + if not json_file_path.is_file(): + print(f"Error: '{json_file_path}' is not a file.", file=sys.stderr) + sys.exit(1) + + try: + # Read and parse the JSON file + with json_file_path.open("r", encoding="utf-8") as f: + data = json.load(f) + + # Validate using Pydantic model + sources = Sources.model_validate(data) + + print(f"✓ Successfully validated {json_file_path}") + print(f" - {len(sources.datasets)} datasets") + print(f" - {len(sources.license_priority)} license priorities") + + except json.JSONDecodeError as e: + print(f"Error: Invalid JSON in '{json_file_path}': {e}", file=sys.stderr) + sys.exit(1) + + except ValidationError as e: + print(f"Error: Validation failed for '{json_file_path}':", file=sys.stderr) + + datasets = None + if isinstance(data, dict): + datasets = data.get("datasets") + + def source_name_for_error(loc: tuple[int | str, ...]) -> str | None: + if not loc: + return None + if loc[0] != "datasets" or len(loc) < 2: + return None + + dataset_index = loc[1] + if not isinstance(dataset_index, int): + return None + + if not isinstance(datasets, list): + return None + if dataset_index < 0 or dataset_index >= len(datasets): + return None + + dataset = datasets[dataset_index] + if not isinstance(dataset, dict): + return None + + source_name = dataset.get("source_name") + if isinstance(source_name, str) and source_name: + return source_name + + return None + + def format_field_path(loc: tuple[int | str, ...]) -> str: + if len(loc) <= 2: + return "" + + field_parts = [] + for part in loc[2:]: + if isinstance(part, str): + if "[" in part or "(" in part or ")" in part: + continue + field_parts.append(part) + # Ignore numeric indices in the path to keep output concise + + return ".".join(field_parts) + + for error in e.errors(): + loc = error["loc"] + source_name = source_name_for_error(loc) + + if source_name: + field_path = format_field_path(loc) + if field_path: + identifier = f"{source_name}: {field_path}" + else: + identifier = source_name + else: + identifier = " -> ".join(str(part) for part in loc) + + print(f" {identifier}: {error['msg']}", file=sys.stderr) + sys.exit(1) + + except Exception as e: + print( + f"Error: Unexpected error processing '{json_file_path}': {e}", + file=sys.stderr, + ) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/packages/overture-schema-sources/src/overture/schema/sources/enums.py b/packages/overture-schema-sources/src/overture/schema/sources/enums.py new file mode 100644 index 000000000..0ed0188ba --- /dev/null +++ b/packages/overture-schema-sources/src/overture/schema/sources/enums.py @@ -0,0 +1,15 @@ +from enum import Enum + + +class BuildSource(str, Enum): + """The ingest source for address data.""" + + OPEN_ADDRESSES = "OpenAddresses" + TF_DATA_PLATFORM = "tf-data-platform" + + +class UpdateType(str, Enum): + """Whether the data is continuously updated upstream or needs manual intervention.""" + + CONTINUOUS = "continuous" + MANUAL = "manual" diff --git a/packages/overture-schema-sources/src/overture/schema/sources/models.py b/packages/overture-schema-sources/src/overture/schema/sources/models.py new file mode 100644 index 000000000..197986c10 --- /dev/null +++ b/packages/overture-schema-sources/src/overture/schema/sources/models.py @@ -0,0 +1,180 @@ +"""Sources schema models for Overture Maps data sources.""" + +from datetime import date +from typing import Annotated, Literal + +from pydantic import Field, HttpUrl + +from overture.schema.core.models import StrictBaseModel +from overture.schema.core.types import CountryCode + +from .enums import BuildSource, UpdateType +from .types import LicenseShortname + + +class Dataset(StrictBaseModel): + """Dataset definition for Overture Maps data sources.""" + + # Required + + source_name: Annotated[ + str, + Field(description="The name of the source."), + ] + source_dataset_name: Annotated[ + str, + Field( + description="The name of the dataset being used from the source. This should match the 'dataset' value found in a record's sources column." + ), + ] + data_url: Annotated[ + HttpUrl | Literal[""], + Field( + description="The data page or data portal of this source, typically includes links to data downloads and license links.", + ), + ] + data_url_archived: Annotated[ + HttpUrl | Literal[""], + Field( + description="URL of the source's data page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", + ), + ] + license_url: Annotated[ + HttpUrl | Literal[""], + Field( + description="A link to this source's data license or page referencing the license associated with the data being imported. This should include explicit license terms.", + ), + ] + license_url_archived: Annotated[ + HttpUrl | Literal[""], + Field( + description="URL of the source's license page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", + ), + ] + license_type: Annotated[ + str, + Field( + description="The license that is associated with the data being used from this source. This should be a valid SPDX license identifier when available." + ), + ] + license_text: Annotated[ + str, + Field( + description="Any relevant license text, direct from the source's license page." + ), + ] + license_attribution: Annotated[ + str, + Field(description="Any attribution required by this source."), + ] + coverage_bbox: Annotated[ + list[float], + Field( + description="The bounding box, in [xmin, ymin, xmax, ymax] format, of this source's coverage.", + min_length=4, + max_length=4, + ), + ] + + # Optional + + inception_date: Annotated[ + date | None, + Field( + description="The first date this source was used in the Overture addresses theme, in YYYY-MM-DD format." + ), + ] = None + url: Annotated[ + HttpUrl | None, + Field(description="The home page of this source."), + ] = None + url_archived: Annotated[ + HttpUrl | None, + Field( + description="URL of the source's home page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", + ), + ] = None + data_download_url: Annotated[ + list[HttpUrl | Literal[""]] | None, + Field( + description="Either a direct download link of data from this source, typically a geo-format or compressed file, or an endpoint from where the data was obtained for use within Overture." + ), + ] = None + countries: Annotated[ + list[CountryCode | Literal["Global"]] | None, + Field( + description="A list of two-character iso country codes that this data source provides data in." + ), + ] = None + coverage_description: Annotated[ + str | None, + Field( + description="A description of the coverage type of the source data - i.e. national, regional, local." + ), + ] = None + data_layer_name: Annotated[ + str | None, + Field(description="Name of the data layer used from this source."), + ] = None + oa_path: Annotated[ + list[str] | None, + Field(description="File path and name in OpenAddresses, if existing."), + ] = None + address_levels: Annotated[ + list[str] | None, + Field( + description="Available address level attributes from OpenAddress, if existing." + ), + ] = None + file_format: Annotated[ + str | None, + Field(description="Format of the file used from this source."), + ] = None + update_frequency: Annotated[ + str | None, + Field(description="How frequently the source data is updated upstream."), + ] = None + build_source: BuildSource | None = None + update_type: UpdateType | None = None + update_schedule: Annotated[ + list[str] | None, + Field( + description="The month or months in which the data is to be re-ingested by the Overture theme using this data source." + ), + ] = None + known_issues: Annotated[ + str | None, + Field( + description="A description of any issues with the data that are known - i.e. data is incomplete, coverage is incomplete, or issues with character encoding." + ), + ] = None + notes: Annotated[ + str | None, + Field( + description="Freeform notes about this data source, including notes on any pre-processing requirements." + ), + ] = None + requires_attribution: Annotated[ + str | None, # TODO should this be a bool? + Field( + description="Whether this source requires attribution to be used in Overture Maps." + ), + ] = None + + +class Sources(StrictBaseModel): + """Common schema definitions for data sources.""" + + # Required + + datasets: Annotated[ + list[Dataset], + Field(description="List of data source entries used by Overture."), + ] + license_priority: Annotated[ + dict[LicenseShortname, Annotated[int, Field(ge=0)]], + Field( + description="Map of license shortnames to their priority (lower number indicates higher priority)." + ), + Field(json_schema_extra={"additionalProperties": False}), + ] diff --git a/packages/overture-schema-sources/src/overture/schema/sources/types.py b/packages/overture-schema-sources/src/overture/schema/sources/types.py new file mode 100644 index 000000000..64dc3626a --- /dev/null +++ b/packages/overture-schema-sources/src/overture/schema/sources/types.py @@ -0,0 +1,11 @@ +from typing import Annotated, NewType + +from pydantic import Field + +LicenseShortname = NewType( + "LicenseShortname", + Annotated[ + str, + Field(pattern=r"^[A-Za-z0-9._+\-]+$"), + ], +) diff --git a/packages/overture-schema-sources/tests/sources_baseline_schema.json b/packages/overture-schema-sources/tests/sources_baseline_schema.json new file mode 100644 index 000000000..f39b00922 --- /dev/null +++ b/packages/overture-schema-sources/tests/sources_baseline_schema.json @@ -0,0 +1,297 @@ +{ + "$defs": { + "BuildSource": { + "description": "The ingest source for address data.", + "enum": [ + "OpenAddresses", + "tf-data-platform" + ], + "title": "BuildSource", + "type": "string" + }, + "Dataset": { + "additionalProperties": false, + "description": "Dataset definition for Overture Maps data sources.", + "properties": { + "address_levels": { + "description": "Available address level attributes from OpenAddress, if existing.", + "items": { + "type": "string" + }, + "title": "Address Levels", + "type": "array" + }, + "build_source": { + "$ref": "#/$defs/BuildSource" + }, + "countries": { + "description": "A list of two-character iso country codes that this data source provides data in.", + "items": { + "anyOf": [ + { + "description": "ISO 3166-1 alpha-2 country code", + "maxLength": 2, + "minLength": 2, + "pattern": "^[A-Z]{2}$", + "type": "string" + }, + { + "const": "Global", + "type": "string" + } + ] + }, + "title": "Countries", + "type": "array" + }, + "coverage_bbox": { + "description": "The bounding box, in [xmin, ymin, xmax, ymax] format, of this source's coverage.", + "items": { + "type": "number" + }, + "maxItems": 4, + "minItems": 4, + "title": "Coverage Bbox", + "type": "array" + }, + "coverage_description": { + "description": "A description of the coverage type of the source data - i.e. national, regional, local.", + "title": "Coverage Description", + "type": "string" + }, + "data_download_url": { + "description": "Either a direct download link of data from this source, typically a geo-format or compressed file, or an endpoint from where the data was obtained for use within Overture.", + "items": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "const": "", + "type": "string" + } + ] + }, + "title": "Data Download Url", + "type": "array" + }, + "data_layer_name": { + "description": "Name of the data layer used from this source.", + "title": "Data Layer Name", + "type": "string" + }, + "data_url": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "const": "", + "type": "string" + } + ], + "description": "The data page or data portal of this source, typically includes links to data downloads and license links.", + "title": "Data Url" + }, + "data_url_archived": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "const": "", + "type": "string" + } + ], + "description": "URL of the source's data page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", + "title": "Data Url Archived" + }, + "file_format": { + "description": "Format of the file used from this source.", + "title": "File Format", + "type": "string" + }, + "inception_date": { + "description": "The first date this source was used in the Overture addresses theme, in YYYY-MM-DD format.", + "format": "date", + "title": "Inception Date", + "type": "string" + }, + "known_issues": { + "description": "A description of any issues with the data that are known - i.e. data is incomplete, coverage is incomplete, or issues with character encoding.", + "title": "Known Issues", + "type": "string" + }, + "license_attribution": { + "description": "Any attribution required by this source.", + "title": "License Attribution", + "type": "string" + }, + "license_text": { + "description": "Any relevant license text, direct from the source's license page.", + "title": "License Text", + "type": "string" + }, + "license_type": { + "description": "The license that is associated with the data being used from this source. This should be a valid SPDX license identifier when available.", + "title": "License Type", + "type": "string" + }, + "license_url": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "const": "", + "type": "string" + } + ], + "description": "A link to this source's data license or page referencing the license associated with the data being imported. This should include explicit license terms.", + "title": "License Url" + }, + "license_url_archived": { + "anyOf": [ + { + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "type": "string" + }, + { + "const": "", + "type": "string" + } + ], + "description": "URL of the source's license page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", + "title": "License Url Archived" + }, + "notes": { + "description": "Freeform notes about this data source, including notes on any pre-processing requirements.", + "title": "Notes", + "type": "string" + }, + "oa_path": { + "description": "File path and name in OpenAddresses, if existing.", + "items": { + "type": "string" + }, + "title": "Oa Path", + "type": "array" + }, + "requires_attribution": { + "description": "Whether this source requires attribution to be used in Overture Maps.", + "title": "Requires Attribution", + "type": "string" + }, + "source_dataset_name": { + "description": "The name of the dataset being used from the source. This should match the 'dataset' value found in a record's sources column.", + "title": "Source Dataset Name", + "type": "string" + }, + "source_name": { + "description": "The name of the source.", + "title": "Source Name", + "type": "string" + }, + "update_frequency": { + "description": "How frequently the source data is updated upstream.", + "title": "Update Frequency", + "type": "string" + }, + "update_schedule": { + "description": "The month or months in which the data is to be re-ingested by the Overture theme using this data source.", + "items": { + "type": "string" + }, + "title": "Update Schedule", + "type": "array" + }, + "update_type": { + "$ref": "#/$defs/UpdateType" + }, + "url": { + "description": "The home page of this source.", + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "title": "Url", + "type": "string" + }, + "url_archived": { + "description": "URL of the source's home page, stored on archive.org, at or near the date the source data was obtained for use within Overture.", + "format": "uri", + "maxLength": 2083, + "minLength": 1, + "title": "Url Archived", + "type": "string" + } + }, + "required": [ + "source_name", + "source_dataset_name", + "data_url", + "data_url_archived", + "license_url", + "license_url_archived", + "license_type", + "license_text", + "license_attribution", + "coverage_bbox" + ], + "title": "Dataset", + "type": "object" + }, + "UpdateType": { + "description": "Whether the data is continuously updated upstream or needs manual intervention.", + "enum": [ + "continuous", + "manual" + ], + "title": "UpdateType", + "type": "string" + } + }, + "additionalProperties": false, + "description": "Common schema definitions for data sources.", + "properties": { + "datasets": { + "description": "List of data source entries used by Overture.", + "items": { + "$ref": "#/$defs/Dataset" + }, + "title": "Datasets", + "type": "array" + }, + "license_priority": { + "additionalProperties": false, + "description": "Map of license shortnames to their priority (lower number indicates higher priority).", + "patternProperties": { + "^[A-Za-z0-9._+\\-]+$": { + "minimum": 0, + "type": "integer" + } + }, + "title": "License Priority", + "type": "object" + } + }, + "required": [ + "datasets", + "license_priority" + ], + "title": "Sources", + "type": "object" +} \ No newline at end of file diff --git a/packages/overture-schema-sources/tests/test_sources_json_schema_baseline.py b/packages/overture-schema-sources/tests/test_sources_json_schema_baseline.py new file mode 100644 index 000000000..20242fe53 --- /dev/null +++ b/packages/overture-schema-sources/tests/test_sources_json_schema_baseline.py @@ -0,0 +1,28 @@ +"""Baseline JSON Schema tests for sources model.""" + +import json +import os + +from overture.schema.core import json_schema +from overture.schema.sources import Sources + + +def test_sources_json_schema_baseline() -> None: + """Ensure Sources model JSON Schema matches the committed baseline.""" + schema = json_schema(Sources) + + baseline_file = os.path.join( + os.path.dirname(__file__), "sources_baseline_schema.json" + ) + + if not os.path.exists(baseline_file): + with open(baseline_file, "w", encoding="utf-8") as f: + json.dump(schema, f, indent=2, sort_keys=True) + + with open(baseline_file, encoding="utf-8") as f: + baseline_schema = json.load(f) + + assert schema == baseline_schema, ( + "Generated JSON Schema differs from baseline. " + "If this change is intentional, delete the baseline file to regenerate it." + ) diff --git a/packages/overture-schema-system/pyproject.toml b/packages/overture-schema-system/pyproject.toml index c61c2d82e..f1f4f8212 100644 --- a/packages/overture-schema-system/pyproject.toml +++ b/packages/overture-schema-system/pyproject.toml @@ -3,7 +3,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "overture-schema-foundation" +name = "overture-schema-system" dynamic = ["version"] description = "Foundational types at the base of the Overture Maps schema system" readme = "README.md" @@ -22,7 +22,7 @@ dev = [ ] [tool.hatch.version] -path = "src/overture/schema/foundation/__about__.py" +path = "src/overture/schema/system/__about__.py" [tool.hatch.build.targets.wheel] packages = ["src/overture"] diff --git a/packages/overture-schema-system/src/overture/schema/system/__init__.py b/packages/overture-schema-system/src/overture/schema/system/__init__.py index 2c131d215..5f9a3fe9c 100644 --- a/packages/overture-schema-system/src/overture/schema/system/__init__.py +++ b/packages/overture-schema-system/src/overture/schema/system/__init__.py @@ -1,6 +1,6 @@ """ -foundation -========== +system +====== Foundational types at the base of the Overture Maps schema system. A set of primitive types, constraint rules, and Pydantic base model classes that diff --git a/reference/examples/sources/basic-sources.yaml b/reference/examples/sources/basic-sources.yaml new file mode 100644 index 000000000..01c7b6520 --- /dev/null +++ b/reference/examples/sources/basic-sources.yaml @@ -0,0 +1,70 @@ +datasets: + - source_name: Example City Open Data + source_dataset_name: address_points + data_url: https://data.example.gov/datasets/address-points + data_url_archived: https://web.archive.org/web/2025/https://data.example.gov/datasets/address-points + license_url: https://data.example.gov/license + license_url_archived: https://web.archive.org/web/2025/https://data.example.gov/license + license_type: CC-BY-4.0 + license_text: | + The City of Example grants permission to use this dataset under the terms of the Creative Commons Attribution 4.0 International License. + license_attribution: City of Example GIS Department + coverage_bbox: + - -122.79 + - 45.43 + - -122.45 + - 45.65 + inception_date: '2022-08-01' + url: https://example.gov + url_archived: https://web.archive.org/web/2025/https://example.gov + data_download_url: + - https://data.example.gov/downloads/address_points.geojson + countries: + - US + coverage_description: Citywide coverage + data_layer_name: address_points + oa_path: + - us/example_city/addresses.csv + address_levels: + - address + file_format: CSV + update_frequency: monthly + build_source: OpenAddresses + update_type: continuous + update_schedule: + - January + - July + known_issues: Source omits new subdivisions until quarterly update. + notes: Dataset normalized to Overture schema during ingest. + requires_attribution: Yes + - source_name: Example Parcel Authority + source_dataset_name: parcel_boundaries + data_url: https://parcels.example.gov/downloads/boundaries + data_url_archived: https://web.archive.org/web/2025/https://parcels.example.gov/downloads/boundaries + license_url: https://parcels.example.gov/license + license_url_archived: https://web.archive.org/web/2025/https://parcels.example.gov/license + license_type: ODbL-1.0 + license_text: | + This dataset is available under the Open Database License v1.0. + license_attribution: Example Parcel Authority + coverage_bbox: + - -122.81 + - 45.38 + - -122.37 + - 45.72 + url: https://parcels.example.gov + data_download_url: + - https://parcels.example.gov/downloads/parcels.gpkg + countries: + - US + coverage_description: Countywide parcel coverage + file_format: GPKG + update_frequency: quarterly + build_source: tf-data-platform + update_type: manual + known_issues: Parcel ownership attributes excluded from public release. + notes: Simplified geometry provided for efficient rendering. + requires_attribution: Required in downstream products. +license_priority: + CC-BY-4.0: 1 + ODbL-1.0: 2 diff --git a/reference/examples/sources/minimal.yaml b/reference/examples/sources/minimal.yaml new file mode 100644 index 000000000..a48862f7e --- /dev/null +++ b/reference/examples/sources/minimal.yaml @@ -0,0 +1,17 @@ +datasets: + - source_name: Address Sample + source_dataset_name: addresses_sample + data_url: https://example.com/addresses + data_url_archived: https://web.archive.org/web/2024/https://example.com/addresses + license_url: https://example.com/addresses/license + license_url_archived: https://web.archive.org/web/2024/https://example.com/addresses/license + license_type: CC-BY-4.0 + license_text: This sample dataset is released under CC-BY-4.0. + license_attribution: Addresses Sample Contributors + coverage_bbox: + - -10.0 + - 50.0 + - -9.5 + - 50.5 +license_priority: + CC-BY-4.0: 1 diff --git a/uv.lock b/uv.lock index 6953f25fe..ae25efaa8 100644 --- a/uv.lock +++ b/uv.lock @@ -14,8 +14,9 @@ members = [ "overture-schema-buildings-theme", "overture-schema-core", "overture-schema-divisions-theme", - "overture-schema-foundation", "overture-schema-places-theme", + "overture-schema-sources", + "overture-schema-system", "overture-schema-transportation-theme", "overture-schema-validation", "overture-schema-workspace", @@ -268,7 +269,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ @@ -632,11 +633,7 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "deepdiff" }, - { name = "mypy" }, - { name = "pytest" }, - { name = "pytest-cov" }, { name = "pyyaml" }, - { name = "ruff" }, { name = "yamlcore" }, ] @@ -656,11 +653,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "deepdiff" }, - { name = "mypy" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "pytest-cov" }, { name = "pyyaml" }, - { name = "ruff" }, { name = "yamlcore", specifier = ">=0.0.4" }, ] @@ -672,26 +665,12 @@ dependencies = [ { name = "pydantic" }, ] -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, -] - [package.metadata] requires-dist = [ { name = "overture-schema-core", editable = "packages/overture-schema-core" }, { name = "pydantic", specifier = ">=2.0" }, ] -[package.metadata.requires-dev] -dev = [ - { name = "mypy" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "ruff" }, -] - [[package]] name = "overture-schema-base-theme" source = { editable = "packages/overture-schema-base-theme" } @@ -700,26 +679,12 @@ dependencies = [ { name = "pydantic" }, ] -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, -] - [package.metadata] requires-dist = [ { name = "overture-schema-core", editable = "packages/overture-schema-core" }, { name = "pydantic", specifier = ">=2.0" }, ] -[package.metadata.requires-dev] -dev = [ - { name = "mypy" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "ruff" }, -] - [[package]] name = "overture-schema-buildings-theme" source = { editable = "packages/overture-schema-buildings-theme" } @@ -773,13 +738,6 @@ dependencies = [ { name = "pydantic" }, ] -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, -] - [package.metadata] requires-dist = [ { name = "overture-schema-core", editable = "packages/overture-schema-core" }, @@ -787,48 +745,43 @@ requires-dist = [ { name = "pydantic", specifier = ">=2.0" }, ] -[package.metadata.requires-dev] -dev = [ - { name = "mypy" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "ruff" }, +[[package]] +name = "overture-schema-places-theme" +source = { editable = "packages/overture-schema-places-theme" } +dependencies = [ + { name = "overture-schema-core" }, + { name = "overture-schema-validation" }, + { name = "pydantic", extra = ["email"] }, +] + +[package.metadata] +requires-dist = [ + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "overture-schema-validation", editable = "packages/overture-schema-validation" }, + { name = "pydantic", specifier = ">=2.0" }, + { name = "pydantic", extras = ["email"] }, ] [[package]] -name = "overture-schema-foundation" -source = { editable = "packages/overture-schema-system" } +name = "overture-schema-sources" +source = { editable = "packages/overture-schema-sources" } dependencies = [ + { name = "overture-schema-core" }, { name = "pydantic" }, - { name = "shapely" }, -] - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, ] [package.metadata] requires-dist = [ - { name = "pydantic", specifier = ">=2.0.0" }, - { name = "shapely", specifier = ">=2.0.0" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "mypy" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "ruff" }, + { name = "overture-schema-core", editable = "packages/overture-schema-core" }, + { name = "pydantic", specifier = ">=2.0" }, ] [[package]] -name = "overture-schema-places-theme" -source = { editable = "packages/overture-schema-places-theme" } +name = "overture-schema-system" +source = { editable = "packages/overture-schema-system" } dependencies = [ - { name = "overture-schema-core" }, - { name = "overture-schema-validation" }, - { name = "pydantic", extra = ["email"] }, + { name = "pydantic" }, + { name = "shapely" }, ] [package.dev-dependencies] @@ -840,10 +793,8 @@ dev = [ [package.metadata] requires-dist = [ - { name = "overture-schema-core", editable = "packages/overture-schema-core" }, - { name = "overture-schema-validation", editable = "packages/overture-schema-validation" }, - { name = "pydantic", specifier = ">=2.0" }, - { name = "pydantic", extras = ["email"] }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "shapely", specifier = ">=2.0.0" }, ] [package.metadata.requires-dev] @@ -875,26 +826,12 @@ dependencies = [ { name = "shapely" }, ] -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, -] - [package.metadata] requires-dist = [ { name = "pydantic", specifier = ">=2.0.0" }, { name = "shapely", specifier = ">=2.0.0" }, ] -[package.metadata.requires-dev] -dev = [ - { name = "mypy" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "ruff" }, -] - [[package]] name = "overture-schema-workspace" version = "0.0.0" @@ -907,6 +844,7 @@ dev = [ { name = "pdoc" }, { name = "pydocstyle" }, { name = "pytest" }, + { name = "pytest-cov" }, { name = "ruff" }, ] @@ -919,6 +857,7 @@ dev = [ { name = "pdoc", specifier = ">=15.0.4" }, { name = "pydocstyle", specifier = ">=6.3.0" }, { name = "pytest", specifier = ">=8.4.1" }, + { name = "pytest-cov" }, { name = "ruff", specifier = ">=0.12.4" }, ]