From cf24007761b57e5c97d6dc18253b9d0326c0a466 Mon Sep 17 00:00:00 2001 From: cathaypacific8747 <58929011+cathaypacific8747@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:59:02 +0800 Subject: [PATCH] chore: add types, setup ci (#62) - original code largely intact. only adding types to check for logic errors. - fix: explicitly use uint16 for enums - fix: `end_time` -> `duration_s` - fix: `global_time` should be `seconds_since_start` - rename: `traffic.ap` -> `traffic.autopilot` - rename: `ConvertHistoricDemo` time -> timestamps - fix misc logic errors - chore: properly run ruff, mypy and tests with uv --- .github/workflows/tests.yml | 127 ++- airtrafficsim/__main__.py | 22 +- airtrafficsim/core/aircraft.py | 177 ++-- airtrafficsim/core/autopilot.py | 160 ++-- airtrafficsim/core/environment.py | 129 +-- airtrafficsim/core/navigation.py | 113 ++- airtrafficsim/core/performance/bada.py | 383 +++++--- airtrafficsim/core/performance/performance.py | 351 ++++--- airtrafficsim/core/traffic.py | 310 +++--- airtrafficsim/core/weather/era5.py | 26 +- airtrafficsim/core/weather/weather.py | 78 +- .../data/environment/ConvertHistoricDemo.py | 37 +- airtrafficsim/data/environment/DemoEnv.py | 15 +- .../data/environment/FullFlightDemo.py | 8 +- airtrafficsim/data/environment/OpenApDemo.py | 23 +- airtrafficsim/data/environment/WeatherDemo.py | 17 +- airtrafficsim/geometry.py | 13 + airtrafficsim/server/__init__.py | 3 + airtrafficsim/server/data.py | 400 ++++---- airtrafficsim/server/replay.py | 64 +- airtrafficsim/server/server.py | 94 +- airtrafficsim/types.py | 14 + airtrafficsim/utils/calculation.py | 50 +- airtrafficsim/utils/enums.py | 118 +-- airtrafficsim/utils/route_detection.py | 102 +- airtrafficsim/utils/unit_conversion.py | 20 +- pyproject.toml | 16 +- tests/test_environment.py | 54 +- tests/test_ui.py | 31 +- uv.lock | 891 ++++++++++++------ 30 files changed, 2321 insertions(+), 1525 deletions(-) create mode 100644 airtrafficsim/geometry.py create mode 100644 airtrafficsim/types.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c610013..9e83cec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,87 +1,72 @@ -name: Tests +name: tests on: + workflow_dispatch: + pull_request_target: push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - build: - + style-check: + name: "style and type checking" + if: (github.event_name != 'pull_request' && ! github.event.pull_request.head.repo.fork) || (github.event_name == 'pull_request' && (github.event.pull_request.head.repo.fork || startsWith(github.head_ref, 'dependabot/'))) + runs-on: ${{ matrix.platform }} strategy: - max-parallel: 5 + fail-fast: false matrix: - include: - - os: ubuntu-latest - label: linux-64 - prefix: /usr/share/miniconda3/envs/airtrafficsim + platform: [ubuntu-latest] + python-version: + - "3.10" + - "3.11" + - "3.12" + - "3.13" - # - os: macos-latest - # label: osx-64 - # prefix: /Users/runner/miniconda3/envs/airtrafficsim + steps: + - uses: actions/checkout@v4 - # - os: windows-latest - # label: win-64 - # prefix: C:\Miniconda3\envs\airtrafficsim + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + enable-cache: true - name: ${{ matrix.label }} - runs-on: ${{ matrix.os }} - - defaults: - run: - shell: bash -el {0} + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - steps: - - uses: actions/checkout@v3 - - - name: Cache conda - uses: actions/cache@v2 - env: - # Increase this value to reset cache if etc/example-environment.yml has not changed - CACHE_NUMBER: 0 - with: - path: ~/conda_pkgs_dir - key: - ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-${{ hashFiles('environment.yml') }} - - - name: Setup miniconda - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: airtrafficsim - channel-priority: strict - environment-file: environment.yml - python-version: 3.11 - use-only-tar-bz2: true # IMPORTANT: This needs to be set for caching to work properly! - - - run: | - conda info - conda list + - name: Install the project + run: uv sync + + - name: Style checking + run: | + uv run ruff check airtrafficsim tests + uv run ruff format --check airtrafficsim tests - - name: Download data from private repository - uses: actions/checkout@v3 - with: - repository: HKUST-OCTAD-LAB/AirTrafficSim-data - ssh-key: ${{ secrets.SSH_KEY }} - path: AirTrafficSim-data + - name: Type checking + run: uv run mypy airtrafficsim tests + - - name: Unzip data - run: | - unzip AirTrafficSim-data/BADA.zip -d airtrafficsim/data/performance/BADA - mv AirTrafficSim-data/.cdsapirc $HOME/ + # testing + - name: Download data from private repository + uses: actions/checkout@v3 + with: + repository: HKUST-OCTAD-LAB/AirTrafficSim-data + ssh-key: ${{ secrets.SSH_KEY }} + path: AirTrafficSim-data - # - name: Lint with flake8 - # run: | - # conda install flake8 - # # stop the build if there are Python syntax errors or undefined names - # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Unzip data + run: | + unzip AirTrafficSim-data/BADA.zip -d airtrafficsim/data/performance/BADA + mv AirTrafficSim-data/.cdsapirc $HOME/ - - name: Test with pytest - run: | - coverage run -m pytest - coverage xml + - name: Run tests + run: | + uv run pytest --cov --cov-report xml - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + - name: Upload coverage to Codecov + if: ${{ github.event_name != 'pull_request_target' }} + uses: codecov/codecov-action@v5 + with: + env_vars: ${{ matrix.python-version }} \ No newline at end of file diff --git a/airtrafficsim/__main__.py b/airtrafficsim/__main__.py index 4731fec..ed24c4c 100644 --- a/airtrafficsim/__main__.py +++ b/airtrafficsim/__main__.py @@ -7,7 +7,7 @@ import airtrafficsim.server.server as server -def main(): +def main() -> None: # Unpack client if ( not Path(__file__) @@ -21,6 +21,7 @@ def main(): ).extractall(Path(__file__).parent.resolve().joinpath("./data/client/")) # Create a parser for command line arguments + # FIXME(abrah): use typer. parser = argparse.ArgumentParser( prog="AirTrafficSim", description="Command line interfaces of AirTrafficSim.", @@ -28,12 +29,18 @@ def main(): parser.add_argument( "--init", type=Path, - help="Create a symbloic link to the data folder: airtrafficsim init .", + help=( + "Create a symbolic link to the data folder: " + "airtrafficsim init ." + ), ) parser.add_argument( "--headless", type=str, - help="Run user defined environment without UI: airtrafficsim --headless .", + help=( + "Run user defined environment without UI: " + "airtrafficsim --headless ." + ), ) args = parser.parse_args() @@ -59,10 +66,12 @@ def main(): ) else: raise IOError( - "The path you provided does not exist. Please provide a valid path." + "The path you provided does not exist. " + "Please provide a valid path." ) else: - # Give error if BADA data is missing TODO: To be removed when OpenAP is implemented + # Give error if BADA data is missing + # TODO: To be removed when OpenAP is implemented if ( len( list( @@ -75,7 +84,8 @@ def main(): <= 1 ): raise IOError( - "BADA folder is empty. Remember to put the BADA performance data into /data/performance/BADA/." + "BADA folder is empty. Remember to put the BADA performance " + "data into /data/performance/BADA/." ) if args.headless: # Run user defined environment without UI diff --git a/airtrafficsim/core/aircraft.py b/airtrafficsim/core/aircraft.py index e403599..8c28942 100644 --- a/airtrafficsim/core/aircraft.py +++ b/airtrafficsim/core/aircraft.py @@ -1,39 +1,47 @@ +from typing import Any + import numpy as np from airtrafficsim.core.navigation import Nav from airtrafficsim.core.traffic import Traffic from airtrafficsim.utils.calculation import Cal -from airtrafficsim.utils.enums import APLateralMode, APThrottleMode +from airtrafficsim.utils.enums import ( + APLateralMode, + APThrottleMode, + Config, + FlightPhase, +) from airtrafficsim.utils.unit_conversion import Unit class Aircraft: """ - Aircraft class to represent the states of one individual aircraft, including get and set functions. + Aircraft class to represent the states of one individual aircraft, including + get and set functions. """ def __init__( self, traffic: Traffic, - call_sign, - aircraft_type, - flight_phase, - configuration, - lat, - long, - alt, - heading, - cas, - fuel_weight, - payload_weight, - departure_airport="", - departure_runway="", - sid="", - arrival_airport="", - arrival_runway="", - star="", - approach="", - flight_plan=[], - cruise_alt=-1, + call_sign: str, + aircraft_type: str, + flight_phase: FlightPhase, + configuration: Config, + lat: float, + long: float, + alt: float, + heading: float, + cas: float, + fuel_weight: float, + payload_weight: float, + departure_airport: str = "", + departure_runway: str = "", + sid: str = "", + arrival_airport: str = "", + arrival_runway: str = "", + star: str = "", + approach: str = "", + flight_plan: list[Any] = [], # FIXME: check correct type + cruise_alt: int = -1, ): """ Initialize one aircraft and add the aircraft to traffic array. @@ -108,7 +116,7 @@ def __init__( ) # Add aircraft. Obtain aircraft index self.vectoring = "" - def set_heading(self, heading): + def set_heading(self, heading: float) -> None: """ Set the heading of the aircraft. @@ -118,10 +126,10 @@ def set_heading(self, heading): Heading [deg] """ index = np.where(self.traffic.index == self.index)[0][0] - self.traffic.ap.heading[index] = heading - self.traffic.ap.lateral_mode[index] = APLateralMode.HEADING + self.traffic.autopilot.heading[index] = heading + self.traffic.autopilot.lateral_mode[index] = APLateralMode.HEADING - def set_speed(self, speed): + def set_speed(self, speed: float) -> None: """ Set the speed of the aircraft. @@ -131,14 +139,10 @@ def set_speed(self, speed): Speed [kt] """ index = np.where(self.traffic.index == self.index)[0][0] - self.traffic.ap.cas[index] = speed - self.traffic.ap.auto_throttle_mode[index] = APThrottleMode.SPEED - - # def set_mach(self, mach): - # """Set Mach [dimensionless]""" - # self.traffic.ap.mach[self.index] = mach + self.traffic.autopilot.cas[index] = speed + self.traffic.autopilot.auto_throttle_mode[index] = APThrottleMode.SPEED - def set_vs(self, vs): + def set_vs(self, vs: float) -> None: """ Set vertical speed. @@ -148,9 +152,9 @@ def set_vs(self, vs): Vertical speed [ft/min] """ index = np.where(self.traffic.index == self.index)[0][0] - self.traffic.ap.vs[index] = vs + self.traffic.autopilot.vs[index] = vs - def set_alt(self, alt): + def set_alt(self, alt: float) -> None: """ Set altitude. @@ -160,9 +164,9 @@ def set_alt(self, alt): Altitude [ft] """ index = np.where(self.traffic.index == self.index)[0][0] - self.traffic.ap.alt[index] = alt + self.traffic.autopilot.alt[index] = alt - def set_direct(self, waypoint): + def set_direct(self, waypoint: str) -> None: """ Set direct to a waypoint. @@ -172,9 +176,11 @@ def set_direct(self, waypoint): ICAO code of the waypoint """ index = np.where(self.traffic.index == self.index)[0][0] - self.traffic.ap.lateral_mode[index] = APLateralMode.LNAV + self.traffic.autopilot.lateral_mode[index] = APLateralMode.LNAV - def set_holding(self, holding_time, holding_fix, region): + def set_holding( + self, holding_time: float, holding_fix: str, region: str + ) -> None: """ Set holding procedure. @@ -182,18 +188,20 @@ def set_holding(self, holding_time, holding_fix, region): ---------- holding_time : float How long should the aircraft hold [second] - holding_fix : float + holding_fix : str ICAO code of the fix that the aircraft should hold - region : float + region : str ICAO code of the region that the aircraft should hold """ index = np.where(self.traffic.index == self.index)[0][0] - self.traffic.ap.holding_round[index] = holding_time - self.traffic.ap.holding_info[index] = Nav.get_holding_procedure( + self.traffic.autopilot.holding_round[index] = holding_time + self.traffic.autopilot.holding_info[index] = Nav.get_holding_procedure( holding_fix, region ) - def set_vectoring(self, vectoring_time, v_2, fix): + def set_vectoring( + self, vectoring_time: float, v_2: float, fix: str + ) -> None: """ Set vectoring procedure. @@ -211,14 +219,16 @@ def set_vectoring(self, vectoring_time, v_2, fix): index = np.where(self.traffic.index == self.index)[0][0] new_dist = ( - self.traffic.ap.dist[index] + self.traffic.autopilot.dist[index] + Unit.kts2mps(self.traffic.cas[index] + v_2) * (vectoring_time) / 2000.0 ) bearing = np.mod( - self.traffic.ap.heading[index] - + np.rad2deg(np.arccos(self.traffic.ap.dist[index] / new_dist)) + self.traffic.autopilot.heading[index] + + np.rad2deg( + np.arccos(self.traffic.autopilot.dist[index] / new_dist) + ) + 360.0, 360.0, ) @@ -230,27 +240,27 @@ def set_vectoring(self, vectoring_time, v_2, fix): ) # Add new virtual waypoint - i = self.traffic.ap.flight_plan_index[index] - self.traffic.ap.flight_plan_lat[index].insert(i, lat) - self.traffic.ap.flight_plan_long[index].insert(i, long) - self.traffic.ap.flight_plan_name[index].insert(i, "VECT") - self.traffic.ap.flight_plan_target_alt[index].insert( - i, self.traffic.ap.flight_plan_target_alt[index][i] + i = self.traffic.autopilot.flight_plan_index[index] + self.traffic.autopilot.flight_plan_lat[index].insert(i, lat) + self.traffic.autopilot.flight_plan_long[index].insert(i, long) + self.traffic.autopilot.flight_plan_name[index].insert(i, "VECT") + self.traffic.autopilot.flight_plan_target_alt[index].insert( + i, self.traffic.autopilot.flight_plan_target_alt[index][i] ) - self.traffic.ap.flight_plan_target_speed[index][i] = v_2 - self.traffic.ap.flight_plan_target_speed[index].insert( - i, self.traffic.ap.flight_plan_target_speed[index][i] + self.traffic.autopilot.flight_plan_target_speed[index][i] = v_2 + self.traffic.autopilot.flight_plan_target_speed[index].insert( + i, self.traffic.autopilot.flight_plan_target_speed[index][i] ) - def resume_own_navigation(self): + def resume_own_navigation(self) -> None: """ Resume own navigation to use autopilot instead of user commanded target. """ index = np.where(self.traffic.index == self.index)[0][0] - self.traffic.ap.lateral_mode[index] = APLateralMode.LNAV - self.traffic.ap.auto_throttle_mode[index] = APThrottleMode.AUTO + self.traffic.autopilot.lateral_mode[index] = APLateralMode.LNAV + self.traffic.autopilot.auto_throttle_mode[index] = APThrottleMode.AUTO - def get_heading(self): + def get_heading(self) -> float: """ Get heading of aircraft. @@ -260,9 +270,9 @@ def get_heading(self): Heading [deg] """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.heading[index] + return float(self.traffic.heading[index]) - def get_cas(self): + def get_cas(self) -> float: """ Get Calibrated air speed of aircraft. @@ -272,9 +282,9 @@ def get_cas(self): Calibrated air speed [knots] """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.cas[index] + return float(self.traffic.cas[index]) - def get_mach(self): + def get_mach(self) -> float: """ Get Mach number of aircraft. @@ -284,9 +294,9 @@ def get_mach(self): Mach number [dimensionless] """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.mach[index] + return float(self.traffic.mach[index]) - def get_vs(self): + def get_vs(self) -> float: """ Get vertical speed of aircraft. @@ -296,9 +306,9 @@ def get_vs(self): Vertical speed [ft/min] """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.vs[index] + return float(self.traffic.vs[index]) - def get_alt(self): + def get_alt(self) -> float: """ Get altitude of aircraft. @@ -308,9 +318,9 @@ def get_alt(self): Altitude [ft] """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.alt[index] + return float(self.traffic.alt[index]) - def get_long(self): + def get_long(self) -> float: """ Get longitude of aircraft. @@ -320,9 +330,9 @@ def get_long(self): Longitude [deg] """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.long[index] + return float(self.traffic.long[index]) - def get_lat(self): + def get_lat(self) -> float: """ Get latitude of aircraft. @@ -332,9 +342,9 @@ def get_lat(self): Latitude [deg] """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.lat[index] + return float(self.traffic.lat[index]) - def get_fuel_consumed(self): + def get_fuel_consumed(self) -> float: """ Get the total fuel consumed of aircraft. @@ -344,9 +354,9 @@ def get_fuel_consumed(self): Fuel consumed [kg] """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.fuel_consumed[index] + return float(self.traffic.fuel_consumed[index]) - def get_next_wp(self): + def get_next_wp(self) -> str: """ Get next waypoint. @@ -356,11 +366,13 @@ def get_next_wp(self): ICAO code of the next waypoing """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.ap.flight_plan_name[index][ - self.traffic.ap.flight_plan_index[index] - ] + return str( + self.traffic.autopilot.flight_plan_name[index][ + self.traffic.autopilot.flight_plan_index[index] + ] + ) - def get_wake(self): + def get_wake(self) -> str: """ Get wake category of aircraft. @@ -370,4 +382,7 @@ def get_wake(self): The ICAO wake category of the aircraft. """ index = np.where(self.traffic.index == self.index)[0][0] - return self.traffic.perf.perf_model._Bada__wake_category[index] + return str( + self.traffic.perf.perf_model._Bada__wake_category[index] # type: ignore + # FIXME(abrah): why access private????? + ) diff --git a/airtrafficsim/core/autopilot.py b/airtrafficsim/core/autopilot.py index fa96b82..985c13f 100644 --- a/airtrafficsim/core/autopilot.py +++ b/airtrafficsim/core/autopilot.py @@ -1,24 +1,31 @@ from __future__ import annotations +from typing import TYPE_CHECKING, Any, NewType + import numpy as np -from airtrafficsim.core.navigation import Nav -from airtrafficsim.utils.calculation import Cal -from airtrafficsim.utils.enums import ( + +from ..types import array +from ..utils.calculation import Cal +from ..utils.enums import ( APLateralMode, APSpeedMode, APThrottleMode, SpeedMode, VerticalMode, ) -from airtrafficsim.utils.unit_conversion import Unit +from ..utils.unit_conversion import Unit +from .navigation import Nav +if TYPE_CHECKING: + from .traffic import Traffic + +AutopilotIdx = NewType("AutopilotIdx", int) +"""Index of the autopilot entry in the arrays""" -class Autopilot: - """ - Autopilot class - """ - def __init__(self): +class Autopilot: + def __init__(self) -> None: + # FIXME(abrah): why 0? also pass in explicit dtype. # Target altitude self.alt = np.zeros([0]) """Autopilot target altitude [feet]""" @@ -60,53 +67,59 @@ def __init__(self): # Flight plan self.flight_plan_index = np.zeros([0], dtype=int) """Index of next waypoint in flight plan array [int]""" - self.flight_plan_name = [] + self.flight_plan_name: list[list[str]] = [] """2D array to store the string of waypoints [[string]]""" - self.flight_plan_lat = [] + self.flight_plan_lat: list[list[float]] = [] """2D array to store the latitude of waypoints [[deg...]]""" - self.flight_plan_long = [] + self.flight_plan_long: list[list[float]] = [] """2D array to store the longitude of waypoints [[deg...]]""" - self.flight_plan_target_alt = [] + self.flight_plan_target_alt: list[list[float]] = [] """2D array of target altitude at each waypoint [[ft...]]""" - self.flight_plan_target_speed = [] + self.flight_plan_target_speed: list[ + list[float] + ] = [] # FIXME(abrah): use algebraic data type for target speed. """2D array of target speed at each waypoint [[cas/mach...]]""" - self.procedure_speed = np.zeros([0]) + + self.procedure_speed: array = np.zeros([0]) """Procedural target speed from BADA""" # Flight mode - self.speed_mode = np.zeros([0]) - """Autopilot speed mode [1: constant Mach, 2: constant CAS, 3: accelerate, 4: decelerate]""" - self.auto_throttle_mode = np.zeros([0]) + self.speed_mode = np.zeros([0], dtype=np.uint16) + """Autopilot speed mode + [1: constant Mach, 2: constant CAS, 3: accelerate, 4: decelerate]""" + self.auto_throttle_mode = np.zeros([0], dtype=np.uint16) """Autothrottle mode [1: Auto, 2: Speed]""" - self.vertical_mode = np.zeros([0]) - """Autopilot vertical mode [1: alt hold, 2: vs mode, 3: flc mode (flight level change), 4. VNAV]""" - self.lateral_mode = np.zeros([0]) - """Autopilot lateral mode [1: heading, 2: LNAV] ATC only use heading, LNAV -> track angle""" + self.vertical_mode = np.zeros([0], dtype=np.uint16) + """Autopilot vertical mode + [1: alt hold, 2: vs mode, 3: flc mode (flight level change), 4. VNAV]""" + self.lateral_mode = np.zeros([0], dtype=np.uint16) + """Autopilot lateral mode + [1: heading, 2: LNAV] ATC only use heading, LNAV -> track angle""" self.expedite_descent = np.zeros([0], dtype=bool) """Autopilot expedite climb setting [bool]""" # Holding self.holding = np.zeros([0], dtype=bool) self.holding_round = np.zeros([0]) - self.holding_info = [] + self.holding_info: list[list[Any]] = [] # FIXME(abrah): what is this??? def add_aircraft( self, - lat, - long, - alt, - heading, - cas, - departure_airport, - departure_runway, - sid, - arrival_airport, - arrival_runway, - star, - approach, - flight_plan, - cruise_alt, - ): + lat: float, + long: float, + alt: float, + heading: float, + cas: float, + departure_airport: str, + departure_runway: str, + sid: str, + arrival_airport: str, + arrival_runway: str, + star: str, + approach: str, + flight_plan: list[str], + cruise_alt: float, + ) -> None: """ Add aircraft and init flight plan @@ -171,7 +184,8 @@ def add_aircraft( speed_resctriction_type, speed_restriction, ) = Nav.get_procedure(departure_airport, departure_runway, sid) - # TODO: Ignored alt restriction 2, alt restriction type, and speed restriction type + # TODO: Ignored alt restriction 2, alt restriction type, + # and speed restriction type self.flight_plan_name[-1].extend(waypoint) self.flight_plan_target_alt[-1].extend(alt_restriction) self.flight_plan_target_speed[-1].extend(speed_restriction) @@ -251,8 +265,12 @@ def add_aircraft( # Add Final Approach flight plan with missed approach removed) # waypoint_idx = waypoint.index(' ') # self.flight_plan_name[-1].extend(waypoint[:waypoint_idx]) - # self.flight_plan_target_alt[-1].extend(alt_restriction_1[:waypoint_idx]) - # self.flight_plan_target_speed[-1].extend(speed_restriction[:waypoint_idx]) + # self.flight_plan_target_alt[-1].extend( + # alt_restriction_1[:waypoint_idx] + # ) + # self.flight_plan_target_speed[-1].extend( + # speed_restriction[:waypoint_idx] + # ) self.flight_plan_name[-1].extend(waypoint) self.flight_plan_target_alt[-1].extend(alt_restriction) self.flight_plan_target_speed[-1].extend(speed_restriction) @@ -294,20 +312,26 @@ def add_aircraft( # Populate alt and speed target from last waypoint if len(self.flight_plan_target_alt[-1]) > 1: - self.flight_plan_target_alt[-1][-1] = 0.0 - for i, val in reversed( - list(enumerate(self.flight_plan_target_alt[-1])) - ): - if val == -1: - self.flight_plan_target_alt[-1][i] = ( - self.flight_plan_target_alt[-1][i + 1] - ) - - # for i, val in reversed(list(enumerate(self.flight_plan_target_speed[n]))): + # self.flight_plan_target_alt[-1][-1] = 0.0 + # for i, val in reversed( + # list(enumerate(self.flight_plan_target_alt[-1])) + # ): + # if val == -1.0: + # self.flight_plan_target_alt[-1][i] = ( + # self.flight_plan_target_alt[-1][i + 1] + # ) + raise NotImplementedError + # NOTE(abrah): unsure what this is doing, commenting out for now. + + # for i, val in reversed(enumerate( + # self.flight_plan_target_speed[n] + # )): # if val == -1: - # self.flight_plan_target_speed[n][i] = self.flight_plan_target_speed[n][i+1] + # self.flight_plan_target_speed[n][i] = ( + # self.flight_plan_target_speed[n][i+1] + # ) - def del_aircraft(self, index): + def del_aircraft(self, index: AutopilotIdx) -> None: """ Delete aircraft @@ -331,7 +355,7 @@ def del_aircraft(self, index): self.hv_next_wp = np.delete(self.hv_next_wp, index) self.dist = np.delete(self.dist, index) self.flight_plan_index = np.delete(self.flight_plan_index, index) - del self.flight_plan_name[index] + del self.flight_plan_name[index] # FIXME(abrah): not necessary. del self.flight_plan_lat[index] del self.flight_plan_long[index] del self.flight_plan_target_alt[index] @@ -346,7 +370,7 @@ def del_aircraft(self, index): self.holding_round = np.delete(self.holding_round, index) del self.holding_info[index] - def update(self, traffic: Traffic): + def update(self, traffic: Traffic) -> None: """ Update the autopilot status for each timestep @@ -473,7 +497,9 @@ def update(self, traffic: Traffic): ) # Waypoint, track angle, and heading - # dist = np.where(self.lateral_mode == AP_lateral_mode.HEADING, 0.0, Calculation.cal_great_circle_distance(traffic.lat, traffic.long, self.lat, self.long)) #km + # dist = np.where(self.lateral_mode == AP_lateral_mode.HEADING, 0.0, + # Calculation.cal_great_circle_distance(traffic.lat, traffic.long, + # self.lat, self.long)) #km dist = Cal.cal_great_circle_dist( traffic.lat, traffic.long, self.lat, self.long ) # km @@ -495,7 +521,8 @@ def update(self, traffic: Traffic): ) # Next track angle to next next waypoint curr_track_angle = Cal.cal_great_circle_bearing( traffic.lat, traffic.long, self.lat, self.long - ) # Current track angle to next waypoint #!TODO consider current heading + ) # Current track angle to next waypoint + #!TODO consider current heading turn_dist = turn_radius * np.tan( np.deg2rad( np.abs(Cal.cal_angle_diff(next_track_angle, curr_track_angle)) @@ -542,9 +569,22 @@ def update(self, traffic: Traffic): ) # Fly over turn - # self.track_angle = np.where(self.lateral_mode == AP_lateral_mode.HEADING, 0.0, np.where(dist<1.0, self.track_angle, Calculation.cal_great_circle_bearing(traffic.lat, traffic.long, self.lat, self.long))) - # self.heading = np.where(self.lateral_mode == AP_lateral_mode.HEADING, self.heading, self.track_angle + np.arcsin(traffic.weather.wind_speed/traffic.tas * np.sin(self.track_angle-traffic.weather.wind_direction))) #https://www.omnicalculator.com/physics/wind-correction-angle - # self.flight_plan_index = np.where((self.lateral_mode == AP_lateral_mode.LNAV) & (dist < 1.0) & (dist > self.dist), self.flight_plan_index+1, self.flight_plan_index) + # self.track_angle = np.where( + # self.lateral_mode == AP_lateral_mode.HEADING, + # 0.0, + # np.where(dist<1.0, + # self.track_angle, + # alculation.cal_great_circle_bearing(traffic.lat, traffic.long, + # self.lat, self.long))) + # self.heading = np.where(self.lateral_mode == AP_lateral_mode.HEADING, + # self.heading, + # self.track_angle + np.arcsin( + # traffic.weather.wind_speed/traffic.tas * np.sin( + # self.track_angle-traffic.weather.wind_direction))) + # #https://www.omnicalculator.com/physics/wind-correction-angle + # self.flight_plan_index = np.where( + # (self.lateral_mode == AP_lateral_mode.LNAV) & (dist < 1.0) & + # (dist > self.dist), self.flight_plan_index+1, self.flight_plan_index) # self.dist = dist # Holding diff --git a/airtrafficsim/core/environment.py b/airtrafficsim/core/environment.py index c5307c4..eedb1e3 100644 --- a/airtrafficsim/core/environment.py +++ b/airtrafficsim/core/environment.py @@ -3,10 +3,13 @@ from datetime import datetime, timedelta, timezone from pathlib import Path +from flask_socketio import SocketIO + import numpy as np import pandas as pd -from airtrafficsim.core.traffic import Traffic -from airtrafficsim.utils.enums import ( + +from ..types import array +from ..utils.enums import ( APLateralMode, APSpeedMode, APThrottleMode, @@ -15,41 +18,45 @@ SpeedMode, VerticalMode, ) -from airtrafficsim.utils.unit_conversion import Unit +from ..utils.unit_conversion import Unit +from .performance.performance import PerformanceMode +from .traffic import Traffic +from .weather.weather import WeatherMode +# FIXME(abrah): single responsibility principle - split this up. class Environment: """ - Base class for simulation environment - + Base class for simulation environment. + inherit this to create a new simulation environment. """ def __init__( self, - file_name, - start_time, - end_time, - weather_mode="ISA", - performance_mode="BADA", + file_name: str, + start_time: datetime, + duration_s: float, + weather_mode: WeatherMode = "ISA", + performance_mode: PerformanceMode = "BADA", ): # User setting self.start_time = start_time """The simulation start time [datetime object]""" - self.end_time = end_time - """The simulation end time [s]""" + self.duration_s = duration_s + """The simulation duration [s]""" # Simulation variable self.traffic = Traffic( - file_name, start_time, end_time, weather_mode, performance_mode + file_name, start_time, duration_s, weather_mode, performance_mode ) - self.global_time = 0 # [s] + self.seconds_since_start: float = 0.0 # [s] # Handle io self.datetime = datetime.now(timezone.utc) self.last_sent_time = time.time() self.graph_type = "None" self.packet_id = 0 - self.buffer_data = [] + self.buffer_data: list[array] = [] # File IO self.file_name = ( @@ -108,19 +115,21 @@ def __init__( self.header.remove("id") self.header.remove("callsign") - def atc_command(self): + # FIXME(abrah): use protocols instead + def atc_command(self) -> None: """ Virtual method to execute user command each timestep. """ pass - def should_end(self): + def should_end(self) -> bool: """ - Virtual method to determine whether the simulation should end each timestep. + Virtual method to determine whether the simulation should end each + timestep. """ return False - def step(self, socketio=None): + def step(self, socketio: SocketIO | None = None) -> None: """ Conduct one simulation timestep. """ @@ -129,15 +138,15 @@ def step(self, socketio=None): # Run atc command self.atc_command() # Run update loop - self.traffic.update(self.global_time) + self.traffic.update(self.seconds_since_start) # Save to file self.save() print( "Environment - step() for global time", - self.global_time, + self.seconds_since_start, "/", - self.end_time, + self.duration_s, "finished at", time.time() - start_time, ) @@ -152,7 +161,7 @@ def step(self, socketio=None): len(self.traffic.index), ( self.start_time - + timedelta(seconds=self.global_time) + + timedelta(seconds=self.seconds_since_start) ).isoformat(timespec="seconds"), ), self.traffic.long, @@ -163,29 +172,30 @@ def step(self, socketio=None): ) self.buffer_data.extend(data) - @socketio.on("setSimulationGraphType") - def set_simulation_graph_type(graph_type): + @socketio.on("setSimulationGraphType") # type: ignore + def set_simulation_graph_type(graph_type: str): self.graph_type = graph_type now = time.time() if ((now - self.last_sent_time) > 0.5) or ( - self.global_time == self.end_time + self.seconds_since_start == self.duration_s ): self.send_to_client(socketio) socketio.sleep(0) self.last_sent_time = now self.buffer_data = [] - self.global_time += 1 + self.seconds_since_start += 1 - def run(self, socketio=None): + def run(self, socketio: SocketIO | None = None) -> None: """ Run the simulation for all timesteps. Parameters ---------- socketio : socketio object, optional - Socketio object to handle communciation when running simulation, by default None + Socketio object to handle communciation when running simulation, by + default None """ if socketio: socketio.emit( @@ -193,12 +203,13 @@ def run(self, socketio=None): {"header": self.header, "file": self.file_name}, ) - for _ in range(self.end_time + 1): + # FIXME: are we sure we want \Delta t = 1s + for _ in range(int(self.duration_s + 1)): # One timestep # Check if the simulation should end if self.should_end(): - self.end_time = self.global_time + self.duration_s = self.seconds_since_start break self.step(socketio) @@ -209,17 +220,18 @@ def run(self, socketio=None): print("") print("Simulation finished") - def save(self): + def save(self) -> None: """ Save all states variable of one timestemp to csv file. """ data = np.column_stack( ( - np.full(len(self.traffic.index), self.global_time), + np.full(len(self.traffic.index), self.seconds_since_start), np.full( len(self.traffic.index), ( - self.start_time + timedelta(seconds=self.global_time) + self.start_time + + timedelta(seconds=self.seconds_since_start) ).isoformat(timespec="seconds"), ), self.traffic.index, @@ -240,37 +252,46 @@ def save(self): self.traffic.perf.drag, self.traffic.perf.esf, self.traffic.accel, - self.traffic.ap.track_angle, - self.traffic.ap.heading, - self.traffic.ap.alt, - self.traffic.ap.cas, - self.traffic.ap.mach, - self.traffic.ap.procedure_speed, - self.traffic.ap.flight_plan_index, + self.traffic.autopilot.track_angle, + self.traffic.autopilot.heading, + self.traffic.autopilot.alt, + self.traffic.autopilot.cas, + self.traffic.autopilot.mach, + self.traffic.autopilot.procedure_speed, + self.traffic.autopilot.flight_plan_index, [ - self.traffic.ap.flight_plan_name[i][val] - if (val < len(self.traffic.ap.flight_plan_name[i])) + self.traffic.autopilot.flight_plan_name[i][val] + if (val < len(self.traffic.autopilot.flight_plan_name[i])) else "NONE" - for i, val in enumerate(self.traffic.ap.flight_plan_index) + for i, val in enumerate( + self.traffic.autopilot.flight_plan_index + ) ], - self.traffic.ap.dist, - self.traffic.ap.holding_round, # autopilot variable + self.traffic.autopilot.dist, + self.traffic.autopilot.holding_round, # autopilot variable [FlightPhase(i).name for i in self.traffic.flight_phase], [Config(i).name for i in self.traffic.configuration], [SpeedMode(i).name for i in self.traffic.speed_mode], [VerticalMode(i).name for i in self.traffic.vertical_mode], - [APSpeedMode(i).name for i in self.traffic.ap.speed_mode], - [APLateralMode(i).name for i in self.traffic.ap.lateral_mode], + [ + APSpeedMode(i).name + for i in self.traffic.autopilot.speed_mode + ], + [ + APLateralMode(i).name + for i in self.traffic.autopilot.lateral_mode + ], [ APThrottleMode(i).name - for i in self.traffic.ap.auto_throttle_mode + for i in self.traffic.autopilot.auto_throttle_mode ], ) ) # mode self.writer.writerows(data) - def export_to_csv(self): + # FIXME(abrah): allow custom path instead. + def export_to_csv(self) -> None: """ Export the simulation result to a csv file. """ @@ -281,7 +302,7 @@ def export_to_csv(self): ) # self.file_path.unlink() - def send_to_client(self, socketio): + def send_to_client(self, socketio: SocketIO) -> None: """ Send the simulation data to client. @@ -301,7 +322,7 @@ def send_to_client(self, socketio): "interval": self.start_time.isoformat() + "/" + ( - self.start_time + timedelta(seconds=self.end_time) + self.start_time + timedelta(seconds=self.duration_s) ).isoformat(), "currentTime": self.start_time.isoformat(), }, @@ -322,7 +343,7 @@ def send_to_client(self, socketio): "interval": time + "/" + ( - self.start_time + timedelta(seconds=self.end_time) + self.start_time + timedelta(seconds=self.duration_s) ).isoformat(), "string": call_sign + "\n" @@ -387,7 +408,7 @@ def send_to_client(self, socketio): "simulationData", { "czml": document, - "progress": self.global_time / self.end_time, + "progress": self.seconds_since_start / self.duration_s, "packet_id": self.packet_id, "graph": graph_data, }, diff --git a/airtrafficsim/core/navigation.py b/airtrafficsim/core/navigation.py index 2c3a155..fb22228 100644 --- a/airtrafficsim/core/navigation.py +++ b/airtrafficsim/core/navigation.py @@ -1,12 +1,27 @@ import csv from pathlib import Path +from typing import Any, NamedTuple from zipfile import ZipFile import numpy as np import pandas as pd -from airtrafficsim.utils.calculation import Cal +from ..types import array +from ..utils.calculation import Cal + +class HoldingProcedure(NamedTuple): + inbound_holding_course: Any + leg_time: Any + leg_length: Any + direction: Any + min_alt: Any + max_alt: Any + speed: Any + + +# FIXME(abrah): do not hardcode paths. +# FIXME(abrah): do not use singleton. class Nav: """ Nav class to provide navigation data from x-plane 11. @@ -29,10 +44,12 @@ class Nav: Minimum off route grid altitudes https://developer.x-plane.com/wp-content/uploads/2020/03/XP-MORA1150-Spec.pdf Nav.min_sector_alt : pandas.dataframe - Minimum sector altitudes for navaids, fixes, airports and runway threshold https://developer.x-plane.com/wp-content/uploads/2020/03/XP-MSA1150-Spec.pdf + Minimum sector altitudes for navaids, fixes, airports & runway threshold + https://developer.x-plane.com/wp-content/uploads/2020/03/XP-MSA1150-Spec.pdf Nav.airports : pandas.dataframe - Airports data (extracted to contain only runway coordinates) https://developer.x-plane.com/article/airport-data-apt-dat-file-format-specification/ + Airports data (extracted to contain only runway coordinates) + https://developer.x-plane.com/article/airport-data-apt-dat-file-format-specification/ Notes ----- @@ -67,7 +84,7 @@ class Nav: # Extract apt.dat to runways.csv and individual csv in xplane/airports/ print("Unpacking airport data (apt.dat). This will take a while...") - airport = [] + airport: list[str] = [] icao = "" alt = 0.0 @@ -110,7 +127,7 @@ class Nav: # Reset if not the end if not row[0] == "99": icao = row[4] - alt = row[1] + alt = float(row[1]) airport = [] # If row code equals to land runway if row[0] == "100": @@ -198,17 +215,23 @@ class Nav: header=None, names=np.arange(0, 26), ) - """Minimum sector altitudes for navaids, fixes, airports and runway threshold https://developer.x-plane.com/wp-content/uploads/2020/03/XP-MSA1150-Spec.pdf""" + """ + Minimum sector altitudes for navaids, fixes, airports and runway threshold + https://developer.x-plane.com/wp-content/uploads/2020/03/XP-MSA1150-Spec.pdf + """ airports = pd.read_csv( Path(__file__) .parent.parent.resolve() .joinpath("./data/navigation/xplane/airports.csv"), header=None, ) - """Airports data (extracted to contain only runway coordinates) https://developer.x-plane.com/article/airport-data-apt-dat-file-format-specification/""" + """ + Airports data (extracted to contain only runway coordinates) + https://developer.x-plane.com/article/airport-data-apt-dat-file-format-specification/ + """ @staticmethod - def get_wp_coord(name, lat, long): + def get_wp_coord(name: str, lat: float, long: float) -> tuple[float, float]: """ Get the nearest waypoint (fix and navaid) coordinate given name. @@ -241,12 +264,17 @@ def get_wp_coord(name, lat, long): wp_long = np.append(fix_long, nav_long) # Find index of minimum distance index = np.argmin( - Cal.cal_great_circle_dist(lat, long, wp_lat, wp_long), axis=0 + Cal.cal_great_circle_dist( + np.array(lat), np.array(long), wp_lat, wp_long + ), + axis=0, ) return wp_lat[index], wp_long[index] @staticmethod - def get_wp_in_area(lat1, long1, lat2, long2): + def get_wp_in_area( + lat1: float, long1: float, lat2: float, long2: float + ) -> tuple[array, list[str]]: """ Get all waypoints(fix, navaids) within area @@ -263,8 +291,7 @@ def get_wp_in_area(lat1, long1, lat2, long2): Returns ------- - [lat, long, name] : [float[], float[], string[]] - [Latitude, Longitude, Name] array of all waypoints in the area + [[lat, long], name] : [[float[], float[]], string[]] """ if lat1 < lat2 and long1 < long2: # If normal condition @@ -362,10 +389,12 @@ def get_wp_in_area(lat1, long1, lat2, long2): .iloc[:, [1, 2, 7]] .to_numpy() ) - return np.vstack((fix, nav)) + return fix, nav @staticmethod - def get_runway_coord(airport, runway): + def get_runway_coord( + airport: str, runway: str + ) -> tuple[float, float, float]: """ Get runway coordinate @@ -384,10 +413,10 @@ def get_runway_coord(airport, runway): """ # TODO: Convert MSL to Geopotentail altitude airport = Nav.airports[(Nav.airports[0].to_numpy() == airport)] - return tuple(airport[airport[1].str.contains(runway)].iloc[0, 2:5]) + return tuple(airport[airport[1].str.contains(runway)].iloc[0, 2:5]) # type: ignore @staticmethod - def find_closest_airport_runway(lat, long): + def find_closest_airport_runway(lat: float, long: float) -> tuple[str, str]: """ Find the closest runway and airport given lat long. @@ -410,12 +439,16 @@ def find_closest_airport_runway(lat, long): & (Nav.airports.iloc[:, 3].between(long - 0.1, long + 0.1)) ] dist = Cal.cal_great_circle_dist( - tmp.iloc[:, 2].to_numpy(), tmp.iloc[:, 3].to_numpy(), lat, long + tmp.iloc[:, 2].to_numpy(), + tmp.iloc[:, 3].to_numpy(), + np.array(lat), + np.array(long), ) - return tmp.iloc[np.argmin(dist)].tolist() + airport, runway = tmp.iloc[np.argmin(dist), 0:2].tolist() + return airport, runway @staticmethod - def get_airport_procedures(airport, procedure_type): + def get_airport_procedures(airport: str, procedure_type: str) -> array: """ Get instrument procedures of an airport. @@ -438,12 +471,19 @@ def get_airport_procedures(airport, procedure_type): .joinpath("./data/navigation/xplane/CIFP/" + airport + ".dat"), header=None, ) - return procedures[procedures[0].str.contains(procedure_type)][ - 2 - ].unique() + procedure_names = procedures[ + procedures[0].str.contains(procedure_type) + ][2].unique() + return procedure_names # type: ignore @staticmethod - def get_procedure(airport, runway, procedure, appch="", iaf=""): + def get_procedure( + airport: str, + runway: str, + procedure: str, + appch: str = "", + iaf: str = "", + ) -> tuple[list[str], list[float], array, list[float], array]: """ Get the details of standard instrument procedure @@ -487,7 +527,8 @@ def get_procedure(airport, runway, procedure, appch="", iaf=""): Note ---- - Terminal procedures (SID/STAR/Approach/Runway) https://developer.x-plane.com/wp-content/uploads/2019/01/XP-CIFP1101-Spec.pd f + Terminal procedures (SID/STAR/Approach/Runway) + https://developer.x-plane.com/wp-content/uploads/2019/01/XP-CIFP1101-Spec.pdf https://wiki.flightgear.org/User:Www2/XP11_Data_Specification """ procedures = pd.read_csv( @@ -523,16 +564,18 @@ def get_procedure(airport, runway, procedure, appch="", iaf=""): if len(index) > 0: procedure_df = procedure_df.loc[: index[0] - 1, :] - alt_restriction_1 = [] - alt_restriction_2 = [] - speed_restriction = [] + alt_restriction_1: list[float] = [] + alt_restriction_2: list[float] = [] + speed_restriction: list[float] = [] for val in procedure_df[23].values: if "FL" in val: alt_restriction_1.append(float(val.replace("FL", "")) * 100.0) else: if val == " ": - alt_restriction_1.append(-1) + alt_restriction_1.append( + -1.0 + ) # FIXME(abrah): use Optional[float] instead else: alt_restriction_1.append(float(val)) @@ -541,13 +584,13 @@ def get_procedure(airport, runway, procedure, appch="", iaf=""): alt_restriction_2.append(float(val.replace("FL", "")) * 100.0) else: if val == " ": - alt_restriction_2.append(-1) + alt_restriction_2.append(-1.0) else: alt_restriction_2.append(float(val)) for val in procedure_df[27].values: if val == " ": - speed_restriction.append(-1) + speed_restriction.append(-1.0) else: speed_restriction.append(float(val)) @@ -558,8 +601,9 @@ def get_procedure(airport, runway, procedure, appch="", iaf=""): alt_restriction_1, ) + # FIXME(abrah): dont index into df with integers return ( - procedure_df[4].values.tolist(), + procedure_df[4].values.tolist(), # type: ignore procedure_df[22].values.tolist(), alt_restriction, procedure_df[26].values.tolist(), @@ -567,7 +611,7 @@ def get_procedure(airport, runway, procedure, appch="", iaf=""): ) @staticmethod - def get_holding_procedure(fix, region): + def get_holding_procedure(fix: str, region: str) -> HoldingProcedure: """ Get holding procedure. @@ -580,7 +624,8 @@ def get_holding_procedure(fix, region): Returns ------- - [inbound holding course, legtime, leg length, direction, min alt, max alt, speed] : [] + [inbound holding course, legtime, leg length, direction, min alt, + max alt, speed] : [] Note ---- @@ -589,4 +634,4 @@ def get_holding_procedure(fix, region): holding = Nav.holding[ (Nav.holding[1] == region) & (Nav.holding[0] == fix) ] - return holding.iloc[0, :].tolist() + return holding.iloc[0, :].tolist() # type: ignore diff --git a/airtrafficsim/core/performance/bada.py b/airtrafficsim/core/performance/bada.py index 54adb2c..f449bc7 100644 --- a/airtrafficsim/core/performance/bada.py +++ b/airtrafficsim/core/performance/bada.py @@ -1,18 +1,28 @@ """Aircraft performance class calculation using BADA 3.15""" +from enum import IntEnum from pathlib import Path import numpy as np -from airtrafficsim.utils.enums import ( + +from ...types import array, uint_array +from ...utils.enums import ( APSpeedMode, Config, EngineType, FlightPhase, VerticalMode, ) -from airtrafficsim.utils.unit_conversion import Unit +from ...utils.unit_conversion import Unit + + +class MassClass(IntEnum): + LO = 1 + AV = 2 + HI = 3 +# FIXME(abrah): split into multiple classes class Bada: """ BADA Performance class @@ -22,7 +32,7 @@ def __init__(self) -> None: """ Initialize BADA performance parameters """ - # ---------------------------- Operations Performance File (OPF) section 3.11 ----------------------------------------- + # 3.11 Operations Performance File (OPF) # Aircraft type self.__n_eng = np.zeros([0]) """Number of engines""" @@ -89,11 +99,13 @@ def __init__(self) -> None: # Engine thrust self.__c_tc_1 = np.zeros([0]) - """1st maximum climb thrust coefficient [Newton (jet/piston) knot-Newton (turboprop)]""" + """1st maximum climb thrust coefficient + [Newton (jet/piston) knot-Newton (turboprop)]""" self.__c_tc_2 = np.zeros([0]) """2nd maximum climb thrust coefficient [feet]""" self.__c_tc_3 = np.zeros([0]) - """3rd maximum climb thrust coefficient [1/feet^2 (jet) Newton (turboprop) knot-Newton (piston)]""" + """3rd maximum climb thrust coefficient + [1/feet^2 (jet) Newton (turboprop) knot-Newton (piston)]""" self.__c_tc_4 = np.zeros([0]) """1st thrust temperature coefficient [K]""" self.__c_tc_5 = np.zeros([0]) @@ -115,7 +127,8 @@ def __init__(self) -> None: # Fuel flow self.__c_f1 = np.zeros([0]) - """1st thrust specific fuel consumption coefficient [kg/(min*kN) (jet) kg/(min*kN*knot) (turboprop) kg/min (piston)]""" + """1st thrust specific fuel consumption coefficient + [kg/(min*kN) (jet) kg/(min*kN*knot) (turboprop) kg/min (piston)]""" self.__c_f2 = np.zeros([0]) """2nd thrust specific fuel consumption coefficient [knots]""" self.__c_f3 = np.zeros([0]) @@ -135,12 +148,13 @@ def __init__(self) -> None: self.__length = np.zeros([0]) """length [m]""" - # ---------------------------- Airline Procedure Models (APF) section 4 ----------------------------------------- + # 4. Airline Procedure Models (APF) # Climb self.__v_cl_1 = np.zeros([0]) """standard climb CAS [knots] between 1,500/6,000 and 10,000 ft""" self.__v_cl_2 = np.zeros([0]) - """standard climb CAS [knots] between 10,000 ft and Mach transition altitude""" + """standard climb CAS [knots] between 10,000 ft and Mach transition + altitude""" self.__m_cl = np.zeros([0]) """standard climb Mach number above Mach transition altitude""" @@ -148,7 +162,8 @@ def __init__(self) -> None: self.__v_cr_1 = np.zeros([0]) """standard cruise CAS [knots] between 3,000 and 10,000 ft""" self.__v_cr_2 = np.zeros([0]) - """standard cruise CAS [knots] between 10,000 ft and Mach transition altitude""" + """standard cruise CAS [knots] between 10,000 ft and Mach transition + altitude""" self.__m_cr = np.zeros([0]) """standard cruise Mach number above Mach transition altitude""" @@ -156,7 +171,8 @@ def __init__(self) -> None: self.__v_des_1 = np.zeros([0]) """standard descent CAS [knots] between 3,000/6,000 and 10,000 ft""" self.__v_des_2 = np.zeros([0]) - """standard descent CAS [knots] between 10,000 ft and Mach transition altitude""" + """standard descent CAS [knots] between 10,000 ft and Mach transition + altitude""" self.__m_des = np.zeros([0]) """standard descent Mach number above Mach transition altitude""" @@ -168,7 +184,7 @@ def __init__(self) -> None: self.descent_schedule = np.zeros([0, 8]) """Standard descent CAS schedule [knots*8] (section 4.3)""" - # ---------------------------- Global Aircraft Parameters (GPF) section 5 ----------------------------------------- + ## 5. Global Aircraft Parameters (GPF) # Read data from GPF file (section 6.8) # 'CD', 1X, A15, 1X, A7, 1X, A16, 1x, A29, 1X, E10.5 if ( @@ -189,111 +205,125 @@ def __init__(self) -> None: ) # Maximum acceleration - self.__A_L_MAX_CIV = GPF[0][5] + self.__A_L_MAX_CIV: float = float(GPF[0][5]) """Maximum longitudinal acceleration for civil flights [2 ft/s^2]""" - self.__A_N_MAX_CIV = GPF[1][5] + self.__A_N_MAX_CIV: float = float(GPF[1][5]) """Maximum normal acceleration for civil flights [5 ft/s^2]""" # Bank angles - self.__PHI_NORM_CIV_TOLD = GPF[2][5] + self.__PHI_NORM_CIV_TOLD: float = float(GPF[2][5]) """Nominal bank angles fpr civil flight during TO and LD [15 deg]""" - self.__PHI_NORM_CIV_OTHERS = GPF[3][5] - """Nominal bank angles for civil flight during all other phases [30 deg]""" - self.__PHI_NORM_MIL = GPF[4][5] + self.__PHI_NORM_CIV_OTHERS: float = float(GPF[3][5]) + """Nominal bank angles for civil flight during all other phases + [30 deg]""" + self.__PHI_NORM_MIL: float = float(GPF[4][5]) """Nominal bank angles for military flight (all phases) [50 deg]""" - self.__PHI_MAX_CIV_TOLD = GPF[5][5] + self.__PHI_MAX_CIV_TOLD: float = float(GPF[5][5]) """Maximum bank angles for civil flight during TO and LD [25 deg]""" - self.__PHI_MAX_CIV_HOLD = GPF[6][5] + self.__PHI_MAX_CIV_HOLD: float = float(GPF[6][5]) """Maximum bank angles for civil flight during HOLD [35 deg]""" - self.__PHI_MAX_CIV_OTHERS = GPF[7][5] - """Maximum bank angles for civil flight during all other phases [45 deg]""" - self.__PHI_MAX_MIL = GPF[8][5] + self.__PHI_MAX_CIV_OTHERS: float = float(GPF[7][5]) + """Maximum bank angles for civil flight during all other phases [45 + deg]""" + self.__PHI_MAX_MIL: float = float(GPF[8][5]) """Maximum bank angles for military flight (all phases) [70 deg]""" - # Expedited descent (drag multiplication factor during expedited descent to simulate use of spoilers) - self.__C_DES_EXP = GPF[9][5] + # Expedited descent (drag multiplication factor during expedited + # descent to simulate use of spoilers) + self.__C_DES_EXP: float = float(GPF[9][5]) """Expedited descent factor [1.6]""" # Thrust factors - self.__C_TCR = GPF[11][5] - """Maximum cruise thrust coefficient [0.95] (postition different between GPF and user menu)""" - self.__C_TH_TO = GPF[10][5] - """Take-off thrust coefficient [1.2] (no longer used since BADA 3.0) (postition different between GPF and user menu)""" + self.__C_TCR: float = float(GPF[11][5]) + """Maximum cruise thrust coefficient [0.95] (postition different + between GPF and user menu)""" + self.__C_TH_TO: float = float(GPF[10][5]) + """Take-off thrust coefficient [1.2] (no longer used since BADA 3.0) + (postition different between GPF and user menu)""" # Configuration altitude threshold - self.__H_MAX_TO = GPF[12][5] + self.__H_MAX_TO: float = float(GPF[12][5]) """Maximum altitude threshold for take-off [400 ft]""" - self.__H_MAX_IC = GPF[13][5] + self.__H_MAX_IC: float = float(GPF[13][5]) """Maximum altitude threshold for initial climb [2,000 ft]""" - self.__H_MAX_AP = GPF[14][5] + self.__H_MAX_AP: float = float(GPF[14][5]) """Maximum altitude threshold for approach [8,000 ft]""" - self.__H_MAX_LD = GPF[15][5] + self.__H_MAX_LD: float = float(GPF[15][5]) """Maximum altitude threshold for landing [3,000 ft]""" # Minimum speed coefficient - self.__C_V_MIN = GPF[16][5] + self.__C_V_MIN: float = float(GPF[16][5]) """Minimum speed coefficient (all other phases) [1.3]""" - self.__C_V_MIN_TO = GPF[17][5] + self.__C_V_MIN_TO: float = float(GPF[17][5]) """Minimum speed coefficient for take-off [1.2]""" # Speed schedules - self.__V_D_CL_1 = GPF[18][5] + self.__V_D_CL_1: float = float(GPF[18][5]) """Climb speed increment below 1,500 ft (jet) [5 knot CAS]""" - self.__V_D_CL_2 = GPF[19][5] + self.__V_D_CL_2: float = float(GPF[19][5]) """Climb speed increment below 3,000 ft (jet) [10 knot CAS]""" - self.__V_D_CL_3 = GPF[20][5] + self.__V_D_CL_3: float = float(GPF[20][5]) """Climb speed increment below 4,000 ft (jet) [30 knot CAS]""" - self.__V_D_CL_4 = GPF[21][5] + self.__V_D_CL_4: float = float(GPF[21][5]) """Climb speed increment below 5,000 ft (jet) [60 knot CAS]""" - self.__V_D_CL_5 = GPF[22][5] + self.__V_D_CL_5: float = float(GPF[22][5]) """Climb speed increment below 6,000 ft (jet) [80 knot CAS]""" - self.__V_D_CL_6 = GPF[23][5] - """Climb speed increment below 500 ft (turbo/piston) [20 knot CAS]""" - self.__V_D_CL_7 = GPF[24][5] - """Climb speed increment below 1,000 ft (turbo/piston) [30 knot CAS]""" - self.__V_D_CL_8 = GPF[25][5] - """ Climb speed increment below 1,500 ft (turbo/piston) [35 knot CAS]""" - self.__V_D_DSE_1 = GPF[26][5] - """Descent speed increment below 1,000 ft (jet/turboprop) [5 knot CAS]""" - self.__V_D_DSE_2 = GPF[27][5] - """Descent speed increment below 1,500 ft (jet/turboprop) [10 knot CAS]""" - self.__V_D_DSE_3 = GPF[28][5] - """Descent speed increment below 2,000 ft (jet/turboprop) [20 knot CAS]""" - self.__V_D_DSE_4 = GPF[29][5] - """Descent speed increment below 3,000 ft (jet/turboprop) [50 knot CAS]""" - self.__V_D_DSE_5 = GPF[30][5] + self.__V_D_CL_6: float = float(GPF[23][5]) + """Climb speed increment below 500 ft (turbo/piston) + [20 knot CAS]""" + self.__V_D_CL_7: float = float(GPF[24][5]) + """Climb speed increment below 1,000 ft (turbo/piston) + [30 knot CAS]""" + self.__V_D_CL_8: float = float(GPF[25][5]) + """ Climb speed increment below 1,500 ft (turbo/piston) + [35 knot CAS]""" + self.__V_D_DSE_1: float = float(GPF[26][5]) + """Descent speed increment below 1,000 ft (jet/turboprop) + [5 knot CAS]""" + self.__V_D_DSE_2: float = float(GPF[27][5]) + """Descent speed increment below 1,500 ft (jet/turboprop) + [10 knot CAS]""" + self.__V_D_DSE_3: float = float(GPF[28][5]) + """Descent speed increment below 2,000 ft (jet/turboprop) + [20 knot CAS]""" + self.__V_D_DSE_4: float = float(GPF[29][5]) + """Descent speed increment below 3,000 ft (jet/turboprop) + [50 knot CAS]""" + self.__V_D_DSE_5: float = float(GPF[30][5]) """Descent speed increment below 500 ft (piston) [5 knot CAS]""" - self.__V_D_DSE_6 = GPF[31][5] + self.__V_D_DSE_6: float = float(GPF[31][5]) """Descent speed increment below 1,000 ft (piston) [10 knot CAS]""" - self.__V_D_DSE_7 = GPF[32][5] + self.__V_D_DSE_7: float = float(GPF[32][5]) """Descent speed increment below 1,500 ft (piston) [20 knot CAS]""" # Holding speeds - self.__V_HOLD_1 = GPF[33][5] + self.__V_HOLD_1: float = float(GPF[33][5]) """Holding speed below FL140 [230 knot CAS]""" - self.__V_HOLD_2 = GPF[34][5] + self.__V_HOLD_2: float = float(GPF[34][5]) """Holding speed between FL140 and FL220 [240 knot CAS]""" - self.__V_HOLD_3 = GPF[35][5] + self.__V_HOLD_3: float = float(GPF[35][5]) """Holding speed between FL220 and FL340 [265 knot CAS]""" - self.__V_HOLD_4 = GPF[36][5] + self.__V_HOLD_4: float = float(GPF[36][5]) """Holding speed above FL340 [0.83 Mach]""" # Ground speed - self.__V_BACKTRACK = GPF[37][5] + self.__V_BACKTRACK: float = float(GPF[37][5]) """Runway backtrack speed [35 knot CAS]""" - self.__V_TAXI = GPF[38][5] + self.__V_TAXI: float = float(GPF[38][5]) """Taxi speed [15 knot CAS]""" - self.__V_APRON = GPF[39][5] + self.__V_APRON: float = float(GPF[39][5]) """Apron speed [10 knot CAS]""" - self.__V_GATE = GPF[40][5] + self.__V_GATE: float = float(GPF[40][5]) """Gate speed [5 knot CAS]""" # Reduced power coefficient - self.__C_RED_TURBO = GPF[42][5] - """Maximum reduction in power for turboprops [0.25] (postition different between GPF and user menu)""" - self.__C_RED_PISTON = GPF[41][5] - """Maximum reduction in power for pistons [0.0] (postition different between GPF and user menu)""" - self.__C_RED_JET = GPF[43][5] + self.__C_RED_TURBO: float = float(GPF[42][5]) + """Maximum reduction in power for turboprops [0.25] + (position different between GPF and user menu)""" + self.__C_RED_PISTON: float = float(GPF[41][5]) + """Maximum reduction in power for pistons [0.0] + (position different between GPF and user menu)""" + self.__C_RED_JET: float = float(GPF[43][5]) """Maximum reduction in power for jets [0.15]""" # Delete variable to free memory @@ -302,7 +332,7 @@ def __init__(self) -> None: else: print("BADA.GPF File does not exit") - # ---------------------------- Atmosphere model section 3.1 ----------------------------------------- + # 3.1 Atmosphere model # MSL Standard atmosphere condition (section 3.1.1) self.__T_0 = 288.15 """Standard atmospheric temperature at MSL [K]""" @@ -323,12 +353,14 @@ def __init__(self) -> None: self.__BETA_T_BELOW_TROP = -0.0065 """ISA temperature gradient with altitude below the tropopause [K/m]""" - # Tropopause (separation between troposphere (below) and stratosphere (above)) + # Tropopause (separation between troposphere (below) and stratosphere + # (above)) self.__H_P_TROP = 11000 """Geopotential pressure altitude [m]""" - # ---------------------------- SYNONYM FILE FORMAT (SYNONYM.NEW) section 6.3 ----------------------------------------- - # | 'CD' | SUPPORT TYPE (-/*) | AIRCRAFT Code | MANUFACTURER | NAME OR MODEL | FILE NAME | ICAO (Y/N) | + # 6.3 Synonym + # | 'CD' | SUPPORT TYPE (-/*) | AIRCRAFT Code | MANUFACTURER | + # NAME OR MODEL | FILE NAME | ICAO (Y/N) | self.__SYNONYM = np.genfromtxt( Path(__file__) .parent.parent.parent.resolve() @@ -350,9 +382,14 @@ def __init__(self) -> None: encoding="unicode_escape", ) - def add_aircraft(self, icao, mass_class=2): + def add_aircraft( + self, + icao: str, + mass_class: MassClass = MassClass.AV, # TODO: useful? + ) -> None: """ - Add one specific aircraft performance data to the performance array according to index. + Add one specific aircraft performance data to the performance array + according to index. Parameters ---------- @@ -363,11 +400,7 @@ def add_aircraft(self, icao, mass_class=2): ICAO code of the specific aircraft. mass_class: int - Aircraft mass for specific flight. To be used for APF. 1 = LO, 2 = AV, 3 = HI TODO: useful? - - Returns - ------- - TODO: + Aircraft mass for specific flight. To be used for APF. """ # Get file name by searching in SYNONYM.NEW @@ -390,8 +423,11 @@ def add_aircraft(self, icao, mass_class=2): skip_footer=1, ) - # 'CD', 3X, A6, 9X, I1, 12X, A9, 17X, A1 - aircraft type block - 1 data line - # | 'CD' | ICAO | # of engine | 'engines' | engine type ( Jet, Turboprop or Piston) | wake category ( J (jumbo), H (heavy), M (medium) or L (light)) + # 'CD', 3X, A6, 9X, I1, 12X, A9, 17X, A1 - aircraft type block - 1 data + # line + # | 'CD' | ICAO | # of engine | 'engines' | + # engine type ( Jet, Turboprop or Piston) | + # wake category ( J (jumbo), H (heavy), M (medium) or L (light)) OPF_Actype = np.genfromtxt( Path(__file__) .parent.parent.parent.resolve() @@ -404,7 +440,8 @@ def add_aircraft(self, icao, mass_class=2): ) # Get data from Airlines Procedures File (Section 6.5) - # 'CD', 25X, 2(I3, 1X), I2, 10X, 2(Ix, 1X), I2, 2X, I2, 2(1X, I3) - procedures specification block - 3 dataline + # 'CD', 25X, 2(I3, 1X), I2, 10X, 2(Ix, 1X), I2, 2X, I2, 2(1X, I3) - + # procedures specification block - 3 dataline APF = np.genfromtxt( Path(__file__) .parent.parent.parent.resolve() @@ -440,7 +477,9 @@ def add_aircraft(self, icao, mass_class=2): self.__n_eng = np.append(self.__n_eng, OPF_Actype.item()[2]) self.__engine_type = np.append( self.__engine_type, - {"Jet": 1, "Turboprop": 2, "Piston": 3}.get(OPF_Actype.item()[4]), + {"Jet": 1, "Turboprop": 2, "Piston": 3}.get( + OPF_Actype.item()[4], 0 + ), # NOTE(abrah): added 0 as None. probably a better way to do this ) self.__wake_category = np.append( self.__wake_category, OPF_Actype.item()[5] @@ -519,9 +558,11 @@ def add_aircraft(self, icao, mass_class=2): del OPF_Actype del OPF - def del_aircraft(self, index): + def del_aircraft(self, index: int) -> None: """ - Delete one specific aircraft performance data to the performance array according to index. This is done by setting all parameters to 0 for reuse in future. + Delete one specific aircraft performance data to the performance array + according to index. + This is done by setting all parameters to 0 for reuse in future. Parameters ---------- @@ -593,7 +634,13 @@ def del_aircraft(self, index): self.cruise_schedule = np.delete(self.cruise_schedule, index, axis=0) self.descent_schedule = np.delete(self.descent_schedule, index, axis=0) - def cal_fuel_burn(self, flight_phase, tas, thrust, alt): + def cal_fuel_burn( + self, + flight_phase: uint_array, + tas: array, + thrust: array, + alt: array, + ) -> array: """ Calculate fuel burn @@ -605,7 +652,7 @@ def cal_fuel_burn(self, flight_phase, tas, thrust, alt): True airspeed [kt] thrust : float[] Thrust [N] - alt : _type_ + alt : float[] Altitude [ft] Returns @@ -638,8 +685,15 @@ def cal_fuel_burn(self, flight_phase, tas, thrust, alt): ) def cal_thrust( - self, vertical_mode, configuration, H_p, V_tas, d_T, drag, ap_speed_mode - ): + self, + vertical_mode: uint_array, + configuration: uint_array, + H_p: array, + V_tas: array, + d_T: array, + drag: array, + ap_speed_mode: uint_array, + ) -> uint_array: """ Calculate thrust given flight phases. @@ -664,13 +718,12 @@ def cal_thrust( Drag forces [N] ap_speed_mode : AP_speed_mode enum [] - Autopilot speed mode [1: Constant CAS, 2: Constant Mach, 3: Acceleration, 4: Deceleration] + Autopilot speed mode [1: Constant CAS, 2: Constant Mach, + 3: Acceleration, 4: Deceleration] - Returns - ------- - _type_ - _description_ """ + vertical_mode = np.array(vertical_mode) + configuration = np.array(configuration) return np.select( condlist=[ (vertical_mode == VerticalMode.CLIMB) @@ -691,7 +744,8 @@ def cal_thrust( ], choicelist=[ self.__cal_max_climb_to_thrust(H_p, V_tas, d_T), - # max climb thrust when acceleration, T = D at cruise, but limited at max cruise thrust + # max climb thrust when acceleration, T = D at cruise, but + # limited at max cruise thrust np.minimum( drag, self.__cal_max_cruise_thrust( @@ -706,13 +760,8 @@ def cal_thrust( ], ) - # ----------------------------------------------------------------------------------------------------- - # ----------------------------- BADA Implementation---------------------------------------------------- - # ----------------------------------------------------------------------------------------------------- - - # ---------------------------- Mass section 3.4 ----------------------------------------- - - def __cal_operating_speed(self, m, V_ref): + # 3.4 Mass + def __cal_operating_speed(self, m: array, V_ref: array) -> array: """ Calculate operating speed given mass (Equation 3.4-1) @@ -729,11 +778,11 @@ def __cal_operating_speed(self, m, V_ref): V: float[] Operating velocity [m/s] """ - return V_ref * np.sqrt(m / (self.__m_ref * 1000.0)) + return V_ref * np.sqrt(m / (self.__m_ref * 1000.0)) # type: ignore - # ---------------------------- Flight envelope section 3.5 ----------------------------------------- + # Flight envelope - def cal_maximum_altitude(self, d_T, m): + def cal_maximum_altitude(self, d_T: array, m: array) -> array: """ Calculate flight envelope (Section 3.5-1) @@ -763,7 +812,7 @@ def cal_maximum_altitude(self, d_T, m): ), ) - def cal_minimum_speed(self, configuration): + def cal_minimum_speed(self, configuration: uint_array) -> array: """ Calculate minimum speed for aircraft (3.5-2~3) @@ -775,7 +824,7 @@ def cal_minimum_speed(self, configuration): Returns ------- v_min: float[] - Minimum at speed at specific configuration [knots] TODO: need to consider mass using __calculate_operating_speed? + Minimum at speed at specific configuration [knots] """ return np.select( @@ -792,13 +841,19 @@ def cal_minimum_speed(self, configuration): self.__C_V_MIN * self.__v_stall_ld, ], default=self.__C_V_MIN * self.__v_stall_cr, - ) + ) # TODO: need to consider mass using __calculate_operating_speed? - # ---------------------------- Aerodynamic section 3.6 ----------------------------------------- + # 3.6 Aerodynamics def cal_aerodynamic_drag( - self, V_tas, bank_angle, m, rho, configuration, c_des_exp - ): + self, + V_tas: array, + bank_angle: array, + m: array, + rho: array, + configuration: uint_array, + c_des_exp: array, + ) -> array: """ Calculate Aerodynamic drag (Section 3.6.1) @@ -871,9 +926,12 @@ def cal_aerodynamic_drag( c_D * rho * np.square(V_tas) * self.__S / 2.0 * c_des_exp, ) - def cal_low_speed_buffeting_limit(self, p, M, m): + def cal_low_speed_buffeting_limit( + self, p: array, M: array, m: array + ) -> array: """ - Low speed buffeting limit for jet and turboprop aircraft. It is expressed as Mach number. (Equation 3.6-6) + Low speed buffeting limit for jet and turboprop aircraft. + It is expressed as Mach number. (Equation 3.6-6) TODO: Appendix B Parameters @@ -894,7 +952,8 @@ def cal_low_speed_buffeting_limit(self, p, M, m): Notes ----- - TODO: Calculate minimum speed for Jet and Turboprop when H_p >= 15000. V_min = MAX(V_min_stall, M_b) (<- same unit) + TODO: Calculate minimum speed for Jet and Turboprop when H_p >= 15000. + V_min = MAX(V_min_stall, M_b) (<- same unit) If H_p < 15000, V_min = V_min_stall """ Q = -np.square(-self.__c_lbo / self.__k) / 9.0 @@ -918,13 +977,16 @@ def cal_low_speed_buffeting_limit(self, p, M, m): ) arr = np.array([X_1, X_2, X_3]) - return np.max(np.where(arr <= 0, np.inf, arr), axis=0) + return np.max(np.where(arr <= 0, np.inf, arr), axis=0) # type: ignore - # ---------------------------- Engine Thrust section 3.7 ----------------------------------------- + ## 3.7 Engine Thrust - def __cal_max_climb_to_thrust(self, H_p, V_tas, d_T): + def __cal_max_climb_to_thrust( + self, H_p: array, V_tas: array, d_T: array + ) -> array: """ - Calculate maximum climb thrust for both take-off and climb phases (Section 3.7.1) + Calculate maximum climb thrust for both take-off and climb phases + (Section 3.7.1) Parameters ---------- @@ -942,7 +1004,8 @@ def __cal_max_climb_to_thrust(self, H_p, V_tas, d_T): Thr_max_climb: float[] Maximum climb thrust [N] """ - # Maximum climb thrust at standard atmosphere conditions (Equations 3.7-1~3) + # Maximum climb thrust at standard atmosphere conditions + # (Equations 3.7-1~3) thr_max_climb_isa = np.select( [ self.__engine_type == EngineType.JET, @@ -960,7 +1023,7 @@ def __cal_max_climb_to_thrust(self, H_p, V_tas, d_T): ) # Corrected for temperature deviation from ISA - return thr_max_climb_isa * ( + thr_max_climb = thr_max_climb_isa * ( 1.0 - np.clip( np.clip(self.__c_tc_5, 0.0, None) * (d_T - self.__c_tc_4), @@ -968,8 +1031,9 @@ def __cal_max_climb_to_thrust(self, H_p, V_tas, d_T): 0.4, ) ) + return thr_max_climb # type: ignore - def __cal_max_cruise_thrust(self, Thr_max_climb): + def __cal_max_cruise_thrust(self, Thr_max_climb: array) -> array: """ Calculate maximum cruise thrust (Equation 3.7-8) @@ -985,11 +1049,18 @@ def __cal_max_cruise_thrust(self, Thr_max_climb): Notes ----- - The normal cruise thrust is by definition set equal to drag (Thr=D). However, the maximum amount of thrust available in cruise situation is limited. + The normal cruise thrust is by definition set equal to drag (Thr=D). + However, the maximum amount of thrust available in cruise situation is + limited. """ return self.__C_TCR * Thr_max_climb - def __cal_descent_thrust(self, H_p, Thr_max_climb, configuration): + def __cal_descent_thrust( + self, + H_p: array, + Thr_max_climb: array, + configuration: uint_array, + ) -> array: """ Calculate descent thrust (Section 3.7.3) @@ -1009,7 +1080,8 @@ def __cal_descent_thrust(self, H_p, Thr_max_climb, configuration): Thr_des: float[] Descent thrust [N] """ - # When “non-clean” data (see Section 3.6.1) is available, H_p,des cannot be below H_max,AP. + # When “non-clean” data (see Section 3.6.1) is available, H_p, + # des cannot be below H_max,AP. return np.where( H_p > np.where( @@ -1034,9 +1106,11 @@ def __cal_descent_thrust(self, H_p, Thr_max_climb, configuration): ), ) # Equation 3.7-12 - # ---------------------------- Reduced climb power section 3.8 ----------------------------------------- + ## 3.8 Reduced climb power - def cal_reduced_climb_power(self, m, H_p, H_max): + def cal_reduced_climb_power( + self, m: array, H_p: array, H_max: array + ) -> array: """ Calculate reduced climb power (section 3.8) @@ -1049,7 +1123,8 @@ def cal_reduced_climb_power(self, m, H_p, H_max): Geopotential pressuer altitude [m] H_max: float[] - Actual maximum altitude for any given mass [kg] (obtained from __cal_maximum_altitude()) + Actual maximum altitude for any given mass [kg] + (obtained from __cal_maximum_altitude()) Returns ------- @@ -1058,7 +1133,7 @@ def cal_reduced_climb_power(self, m, H_p, H_max): Notes ----- - The result can be applied in the calculation of ROCD during climb phase TODO: + The result can be applied in the calculation of ROCD during climb phase """ c_red = np.where( H_p < 0.8 * H_max, @@ -1080,11 +1155,12 @@ def cal_reduced_climb_power(self, m, H_p, H_max): self.__m_max - self.m_min ) - # ---------------------------- Fuel consumption section 3.9 ----------------------------------------- + ## 3.9 Fuel consumption - def __cal_nominal_fuel_flow(self, V_tas, Thr): + def __cal_nominal_fuel_flow(self, V_tas: array, Thr: array) -> array: """ - Calculate nominal fuel flow (except idle descent and cruise) (equations 3.9-1~3 and 3.9-7) + Calculate nominal fuel flow (except idle descent and cruise) + (equations 3.9-1~3 and 3.9-7) Parameters ---------- @@ -1118,7 +1194,7 @@ def __cal_nominal_fuel_flow(self, V_tas, Thr): ], ) # Equation 3.9-7 - def __cal_minimum_fuel_flow(self, H_p): + def __cal_minimum_fuel_flow(self, H_p: array) -> array: """ Calculate fuel flow for idle descent (equations 3.9-4 and 3.9-8) @@ -1141,7 +1217,9 @@ def __cal_minimum_fuel_flow(self, H_p): self.__c_f3, ) # Equation 3.9-8 - def __cal_approach_landing_fuel_flow(self, V_tas, Thr, H_p): + def __cal_approach_landing_fuel_flow( + self, V_tas: array, Thr: array, H_p: array + ) -> array: """ Calculate fuel flow for approach and landing (equations 3.9-5 and 3.9-8) @@ -1172,7 +1250,7 @@ def __cal_approach_landing_fuel_flow(self, V_tas, Thr, H_p): self.__cal_minimum_fuel_flow(H_p), ) # Equation 3.9-8 - def __cal_cruise_fuel_flow(self, V_tas, Thr): + def __cal_cruise_fuel_flow(self, V_tas: array, Thr: array) -> array: """ Calculate fuel flow for cruise (equations 3.9-6 and 3.9-9) @@ -1210,11 +1288,12 @@ def __cal_cruise_fuel_flow(self, V_tas, Thr): ], ) # Equation 3.9-9 - # ---------------------------- Airline Procedure Models section 4 ----------------------------------------- + ## 4. Airline Procedure Models - def init_procedure_speed(self, m, n): + def init_procedure_speed(self, m: array, n: int) -> None: """ - Initialize standard air speed schedule for all flight phases (Section 4.1-4.3) + Initialize standard air speed schedule for all flight phases + (Section 4.1-4.3) Parameters ---------- @@ -1304,7 +1383,9 @@ def init_procedure_speed(self, m, n): 0.0, ] - def get_procedure_speed(self, H_p, H_p_trans, flight_phase): + def get_procedure_speed( + self, H_p: array, H_p_trans: array, flight_phase: uint_array + ) -> array: """ Get the standard air speed schedule @@ -1329,8 +1410,10 @@ def get_procedure_speed(self, H_p, H_p_trans, flight_phase): Notes ----- - TODO: Recommended to determine the speed schedule from the highest altitude to the lowest one, - and to use at each step the speed of the higher altitude range as a ceiling value for the lower altitude range. + TODO: Recommended to determine the speed schedule from the highest + altitude to the lowest one, + and to use at each step the speed of the higher altitude range as a + ceiling value for the lower altitude range. TODO: Bound the speed schedule form the minimum and maximum speed. """ @@ -1474,7 +1557,9 @@ def get_procedure_speed(self, H_p, H_p_trans, flight_phase): ], ) - def update_configuration(self, V_cas, H_p, vertical_mode): + def update_configuration( + self, V_cas: array, H_p: array, vertical_mode: uint_array + ) -> uint_array: """ Update configuration (section 3.5) @@ -1529,8 +1614,8 @@ def update_configuration(self, V_cas, H_p, vertical_mode): default=Config.CLEAN, ) - # ---------------------------- Global aircraft parameters section 5 ----------------------------------------- - def cal_max_d_tas(self, d_t): + ## 5. Global aircraft parameters + def cal_max_d_tas(self, d_t: array) -> array: """ Calculate maximum delta true air speed (equation 5.2-1) @@ -1546,7 +1631,7 @@ def cal_max_d_tas(self, d_t): """ return self.__A_L_MAX_CIV * d_t - def cal_max_d_rocd(self, d_t, V_tas, rocd): + def cal_max_d_rocd(self, d_t: array, V_tas: array, rocd: array) -> array: """ Calculate maximum delta rate of climb or descend (equation 5.2-2) @@ -1566,13 +1651,15 @@ def cal_max_d_rocd(self, d_t, V_tas, rocd): d_rocd: float[] Delta rate of climb or descent [ft/s^2] """ - return np.sin( + d_rocd = np.sin( np.arcsin(rocd / V_tas) - self.__A_N_MAX_CIV * d_t / V_tas ) * (V_tas + d_t) + return d_rocd # type: ignore - def cal_expedite_descend_factor(self, expedite_descent): + def cal_expedite_descend_factor(self, expedite_descent: array) -> array: """ - Calculate expedited descent factor for drag multiplication. (Equation 5.4-1) + Calculate expedited descent factor for drag multiplication. + (Equation 5.4-1) Parameters ---------- diff --git a/airtrafficsim/core/performance/performance.py b/airtrafficsim/core/performance/performance.py index 95c5a17..895074d 100644 --- a/airtrafficsim/core/performance/performance.py +++ b/airtrafficsim/core/performance/performance.py @@ -1,28 +1,27 @@ """Performance base class""" +# FIXME(abrah): move entire thing to __init__.py +from typing import TYPE_CHECKING, Any, Literal + +# FIXME(abrah): openap should be optional. from openap import WRAP, Drag, FuelFlow, Thrust, prop import numpy as np -from airtrafficsim.core.performance.bada import Bada -from airtrafficsim.utils.enums import APSpeedMode, Config, VerticalMode -from airtrafficsim.utils.unit_conversion import Unit +from ...types import ArrayOrFloat, array, uint_array +from ...utils.enums import APSpeedMode, Config, VerticalMode +from ...utils.unit_conversion import Unit +from .bada import Bada, MassClass -class Performance: - """ - Performance base class - """ +if TYPE_CHECKING: + from ..traffic import Traffic - def __init__(self, performance_mode): - """ - Initialize Performance base class +PerformanceMode = Literal["BADA", "OpenAP"] - Parameters - ---------- - performance_mode : string, optional - Which performance model to use [BADA, OpenAP] - """ +class Performance: + # FIXME(abrah): don't mix BADA and OpenAP. + def __init__(self, performance_mode: PerformanceMode) -> None: self.performance_mode = performance_mode """Whether BADA performance model is used [string]""" @@ -30,18 +29,13 @@ def __init__(self, performance_mode): self.perf_model = Bada() else: # OpenAP - self.prop_model = [] - self.thrust_model = [] - self.drag_model = [] - self.fuel_flow_model = [] - self.wrap_model = [] - - # self.prop_model = np.empty([N], dtype=np.void) - # self.thrust_model = np.empty([N], dtype=np.void) - # self.drag_model = np.empty([N], dtype=np.void) - # self.fuel_flow_model = np.empty([N], dtype=np.void) - # self.wrap_model = np.empty([N], dtype=np.void) + self.prop_model: list[dict[str, Any]] = [] + self.thrust_model: list[Thrust] = [] + self.drag_model: list[Drag] = [] + self.fuel_flow_model: list[FuelFlow] = [] + self.wrap_model: list[WRAP] = [] + # FIXME(abrah): why are we storing state here? self.drag = np.zeros([0]) """Drag [N]""" self.thrust = np.zeros([0]) @@ -49,7 +43,7 @@ def __init__(self, performance_mode): self.esf = np.zeros([0]) """Energy share factor [dimensionless]""" - # ---------------------------- Atmosphere model (Ref: BADA user menu section 3.1) ----------------------------------------- + ## Atmosphere model (Ref: BADA user menu section 3.1) # MSL Standard atmosphere condition self.__T_0 = 288.15 """Standard atmospheric temperature at MSL [K]""" @@ -68,11 +62,15 @@ def __init__(self, performance_mode): """Gravitational acceleration [m/s^2]""" self.__BETA_T_BELOW_TROP = -0.0065 """ISA temperature gradient with altitude below the tropopause [K/m]""" - # Tropopause (separation between troposphere (below) and stratosphere (above)) + # Tropopause (separation between troposphere (below) + # and stratosphere (above)) self.__H_P_TROP = 11000 """Geopotential pressure altitude [m]""" - def add_aircraft(self, icao, engine=None, mass_class=2): + # FIXME(abrah): mass class should be enum + def add_aircraft( + self, icao: str, mass_class: MassClass = MassClass.AV + ) -> None: """ Add an aircraft to traffic array. @@ -98,7 +96,7 @@ def add_aircraft(self, icao, engine=None, mass_class=2): ) self.wrap_model.append(WRAP(ac=icao)) - def del_aircraft(self, index): + def del_aircraft(self, index: int) -> None: """ Delete an aircraft from traffic array. """ @@ -114,9 +112,10 @@ def del_aircraft(self, index): del self.fuel_flow_model[index] del self.wrap_model[index] - def init_procedure_speed(self, mass, n): + def init_procedure_speed(self, mass: array, n: int) -> None: """ - Initialize standard air speed schedule for all flight phases (Section 4.1-4.3) + Initialize standard air speed schedule for all flight phases + (Section 4.1-4.3) Parameters ---------- @@ -129,7 +128,9 @@ def init_procedure_speed(self, mass, n): if self.performance_mode == "BADA": self.perf_model.init_procedure_speed(mass, n) - def get_procedure_speed(self, H_p, H_p_trans, flight_phase): + def get_procedure_speed( + self, H_p: array, H_p_trans: array, flight_phase: uint_array + ) -> array: """ Get the standard air speed schedule @@ -152,6 +153,7 @@ def get_procedure_speed(self, H_p, H_p_trans, flight_phase): M_std: float[] Standard Mach [dimensionless] """ + # FIXME(abrah): dont return v or M!!! if self.performance_mode == "BADA": return self.perf_model.get_procedure_speed( H_p, H_p_trans, flight_phase @@ -159,9 +161,9 @@ def get_procedure_speed(self, H_p, H_p_trans, flight_phase): else: return np.where(H_p > 0, 20000, 20000) - # ---------------------------- Atmosphere model (Ref: BADA user menu section 3.1) ----------------------------------------- + ### Atmosphere model (Ref: BADA user menu section 3.1) - def cal_temperature(self, H_p, d_T): + def cal_temperature(self, H_p: array | float, d_T: array | float) -> array: """ Calculate Temperature (Equation 3.1-12~16) @@ -189,7 +191,9 @@ def cal_temperature(self, H_p, d_T): self.__T_0 + d_T + self.__BETA_T_BELOW_TROP * self.__H_P_TROP, ) - def cal_air_pressure(self, H_p, T, d_T): + def cal_air_pressure( + self, H_p: array | float, T: array, d_T: array + ) -> array: r""" Calculate Air Pressure (Equation 3.1-17~20) @@ -214,13 +218,15 @@ def cal_air_pressure(self, H_p, T, d_T): """ return np.where( H_p <= self.__H_P_TROP, - # If below or equal Geopotential pressure altitude of tropopause (Equation 3.1-18) + # If below or equal Geopotential pressure altitude of tropopause + # (Equation 3.1-18) self.__P_0 * np.power( (T - d_T) / self.__T_0, -self.__G_0 / (self.__BETA_T_BELOW_TROP * self.__R), ), - # If above Geopotential pressure altitude of tropopause (Equation 3.1-20) + # If above Geopotential pressure altitude of tropopause + # (Equation 3.1-20) self.__P_0 * np.power( (self.cal_temperature(self.__H_P_TROP, d_T) - d_T) / self.__T_0, @@ -233,7 +239,7 @@ def cal_air_pressure(self, H_p, T, d_T): ), ) - def cal_air_density(self, p, T): + def cal_air_density(self, p: ArrayOrFloat, T: ArrayOrFloat) -> ArrayOrFloat: """ Calculate Air Density (Equation 3.1-21) @@ -252,7 +258,7 @@ def cal_air_density(self, p, T): """ return p / (self.__R * T) - def cal_speed_of_sound(self, T): + def cal_speed_of_sound(self, T: ArrayOrFloat) -> ArrayOrFloat: """ Calculate speed of sound. (Equation 3.1-22) @@ -266,9 +272,11 @@ def cal_speed_of_sound(self, T): a: float[] Speed of sound [m/s] """ - return np.sqrt(self.__KAPPA * self.__R * T) + return (self.__KAPPA * self.__R * T) ** 0.5 # type: ignore - def cas_to_tas(self, V_cas, p, rho): + def cas_to_tas( + self, V_cas: ArrayOrFloat, p: ArrayOrFloat, rho: ArrayOrFloat + ) -> ArrayOrFloat: """ Convert Calibrated air speed to True air speed. (Equation 3.1-23) @@ -289,36 +297,33 @@ def cas_to_tas(self, V_cas, p, rho): True air speed [m/s] """ mu = (self.__KAPPA - 1) / self.__KAPPA - return np.power( - 2.0 - / mu - * p - / rho + V_tas = ( + (2.0 / mu * p / rho) * ( - np.power( + ( 1.0 + self.__P_0 / p * ( - np.power( + ( 1.0 + mu / 2.0 * self.__RHO_0 / self.__P_0 - * np.square(V_cas), - 1.0 / mu, + * np.square(V_cas) ) + ** (1.0 / mu) - 1 - ), - mu, + ) ) + ** mu - 1 - ), - 0.5, - ) + ) + ) ** 0.5 + return V_tas # type: ignore - def tas_to_cas(self, V_tas, p, rho): + def tas_to_cas(self, V_tas: array, p: array, rho: array) -> array: """ Convert True air speed to Calibrated air speed. (Equation 3.1-24) @@ -339,7 +344,7 @@ def tas_to_cas(self, V_tas, p, rho): Calibrated air speed [m/s] """ mu = (self.__KAPPA - 1) / self.__KAPPA - return np.power( + V_cas = np.power( 2 / mu * self.__P_0 @@ -361,8 +366,9 @@ def tas_to_cas(self, V_tas, p, rho): ), 0.5, ) + return V_cas # type: ignore - def mach_to_tas(self, M, T): + def mach_to_tas(self, M: array, T: array) -> array: """ Convert Mach number to True Air speed (Equation 3.1-26) @@ -379,9 +385,9 @@ def mach_to_tas(self, M, T): V_tas: float[] True air speed [m/s] """ - return M * np.sqrt(self.__KAPPA * self.__R * T) + return M * np.sqrt(self.__KAPPA * self.__R * T) # type: ignore - def tas_to_mach(self, V_tas, T): + def tas_to_mach(self, V_tas: ArrayOrFloat, T: ArrayOrFloat) -> ArrayOrFloat: """ Convert True Air speed to Mach number (Equation 3.1-26) @@ -398,10 +404,10 @@ def tas_to_mach(self, V_tas, T): M: float[] Mach number [dimensionless] """ - return V_tas / np.sqrt(self.__KAPPA * self.__R * T) + return V_tas / (self.__KAPPA * self.__R * T) ** 0.5 # type: ignore - # ---------------------------- Operation limit ----------------------------------------- - def cal_transition_alt(self, n, d_T): + ### Operation limit + def cal_transition_alt(self, n: int, d_T: array) -> array: """ Calculate Mach/CAS transition altitude. (Equation 3.1-27~28) @@ -417,9 +423,10 @@ def cal_transition_alt(self, n, d_T): Note ---- - Transition altitude is defined to be the geopotential pressure altitude at which V_CAS and M represent the same TAS value. - TODO: Separate climb and descent trans altitude? + Transition altitude is defined to be the geopotential pressure altitude + at which V_CAS and M represent the same TAS value. """ + # TODO: Separate climb and descent trans altitude? if self.performance_mode == "BADA": V_cas = Unit.kts2mps(self.perf_model.climb_schedule[n, -2]) M = self.perf_model.climb_schedule[n, -1] @@ -469,11 +476,12 @@ def cal_transition_alt(self, n, d_T): ) else: - return ( - self.wrap_model[n].climb_cross_alt_conmach()["default"] * 1000.0 - ) + wrap = self.wrap_model[n] + if not isinstance(wrap, WRAP): + raise RuntimeError("panic: wrap_model is not WRAP") + return wrap.climb_cross_alt_conmach()["default"] * 1000.0 # type: ignore - def get_empty_weight(self, n): + def get_empty_weight(self, n: int) -> float: """ Get Empty Weight of an aircraft @@ -488,11 +496,11 @@ def get_empty_weight(self, n): Empty weight(BADA) or Operating empty weight(OpenAP) [kg] """ if self.performance_mode == "BADA": - return self.perf_model.m_min[n] * 1000.0 + return self.perf_model.m_min[n] * 1000.0 # type: ignore else: - return self.prop_model[n]["limits"]["OEW"] + return self.prop_model[n]["limits"]["OEW"] # type: ignore - def cal_maximum_alt(self, d_T, m): + def cal_maximum_alt(self, d_T: array, m: array) -> array: """ Calculate maximum altitude @@ -516,7 +524,7 @@ def cal_maximum_alt(self, d_T, m): np.array([x["limits"]["ceiling"] for x in self.prop_model]) ) - def cal_maximum_speed(self): + def cal_maximum_speed(self) -> tuple[array, array]: """ Calculate maximum altitude @@ -532,7 +540,7 @@ def cal_maximum_speed(self): len(self.prop_model), 1000 ) - def cal_minimum_speed(self, configuration): + def cal_minimum_speed(self, configuration: uint_array) -> array: """ Calculate minimum speed @@ -549,9 +557,10 @@ def cal_minimum_speed(self, configuration): if self.performance_mode == "BADA": return self.perf_model.cal_minimum_speed(configuration) else: - return 0.0 # TODO: OpenAP no minimum/stall speed? + # NOTE(abrah): should be resolved when we split the two + raise RuntimeError("panic: OpenAP has no minimum/stall speed") - def cal_max_d_tas(self, d_t): + def cal_max_d_tas(self, d_t: array) -> array: """ Calculate maximum delta true air speed @@ -570,7 +579,7 @@ def cal_max_d_tas(self, d_t): else: return 2 * d_t - def cal_max_d_rocd(self, d_t, V_tas, rocd): + def cal_max_d_rocd(self, d_t: array, V_tas: array, rocd: array) -> array: """ Calculate maximum delta rate of climb or descend (equation 5.2-2) @@ -593,16 +602,23 @@ def cal_max_d_rocd(self, d_t, V_tas, rocd): if self.performance_mode == "BADA": return self.perf_model.cal_max_d_rocd(d_t, V_tas, rocd) else: - return np.sin(np.arcsin(rocd / V_tas) - 5.0 * d_t / V_tas) * ( + d_rocd = np.sin(np.arcsin(rocd / V_tas) - 5.0 * d_t / V_tas) * ( V_tas + d_t ) + return d_rocd # type: ignore - # ---------------------------- Performance ----------------------------------------- - # ---------------------------- Total-Energy Model Section 3.2 ----------------------------------------- + ## Performance + ### Total Energy Model (3.2) def cal_energy_share_factor( - self, H_p, T, d_T, M, ap_speed_mode, vertical_mode - ): + self, + H_p: array, + T: array, + d_T: array, + M: array, + ap_speed_mode: uint_array, + vertical_mode: uint_array, + ) -> array: """ Calculate energy share factor (Equation 3.2-5, 8~11) @@ -642,9 +658,11 @@ def cal_energy_share_factor( # Constant Mach np.where( H_p > self.__H_P_TROP, - # Conditiona a: Constant Mach number in stratosphere (Equation 3.2-8) + # Condition a: Constant Mach number in stratosphere + # (Equation 3.2-8) 1.0, - # Condition b: Constant Mach number below tropopause (Equation 3.2-9) + # Condition b: Constant Mach number below tropopause + # (Equation 3.2-9) np.power( 1.0 + self.__KAPPA @@ -661,7 +679,8 @@ def cal_energy_share_factor( # Constnt CAS np.where( H_p <= self.__H_P_TROP, - # Condition c: Constant Calibrated Airspeed (CAS) below tropopause (Equation 3.2-10) + # Condition c: Constant Calibrated Airspeed (CAS) below + # tropopause (Equation 3.2-10) np.power( 1.0 + self.__KAPPA @@ -685,7 +704,8 @@ def cal_energy_share_factor( ), -1.0, ), - # Condition d: Constant Calibrated Airspeed (CAS) above tropopause (Equation 3.2-11) + # Condition d: Constant Calibrated Airspeed (CAS) above + # tropopause (Equation 3.2-11) np.power( 1.0 + np.power( @@ -711,10 +731,22 @@ def cal_energy_share_factor( ], ) - def cal_tem_rocd(self, T, d_T, m, D, f_M, Thr, V_tas, C_pow_red): + def cal_tem_rocd( + self, + T: array, + d_T: array, + m: array, + D: array, + f_M: array, + Thr: array, + V_tas: array, + C_pow_red: array, + ) -> array: """ - Total Energy Model. Speed and Throttle Controller. (BADA User Menu Equation 3.2-1a and 3.2-7) - Calculate Rate of climb or descent given velocity(constant) and thrust (max climb thrust/idle descent). + Total Energy Model. Speed and Throttle Controller. + (BADA User Menu Equation 3.2-1a and 3.2-7) + Calculate Rate of climb or descent given velocity(constant) and thrust + (max climb thrust/idle descent). Parameters ---------- @@ -746,15 +778,28 @@ def cal_tem_rocd(self, T, d_T, m, D, f_M, Thr, V_tas, C_pow_red): ------- rocd: float[] Rate of climb or descent [m/s] - Defined as variation with time of the aircraft geopotential pressure altitude H_p + Defined as variation with time of the aircraft geopotential pressure + altitude H_p """ - return ( + rocd = ( (T - d_T) / T * (Thr - D) * V_tas * C_pow_red / m / self.__G_0 * f_M ) - - def cal_tem_accel(self, T, d_T, m, D, rocd, Thr, V_tas): + return rocd + + def cal_tem_accel( + self, + T: array, + d_T: array, + m: array, + D: array, + rocd: array, + Thr: array, + V_tas: array, + ) -> array: + # NOTE: changed to equation 3.2-1 """ - Total Energy Model. ROCD and Throttle Controller. (BADA User Menu Equation 3.2-1b and 3.2-7) NOTE: changed to equation 3.2-1 + Total Energy Model. ROCD and Throttle Controller. + (BADA User Menu Equation 3.2-1b and 3.2-7) Calculate accel given ROCD and thrust. Parameters @@ -792,9 +837,20 @@ def cal_tem_accel(self, T, d_T, m, D, rocd, Thr, V_tas): (Thr - D) / m - self.__G_0 / V_tas * rocd * T / (T - d_T), ) - def cal_tem_thrust(self, T, d_T, m, D, f_M, rocd, V_tas): + def cal_tem_thrust( + self, + T: array, + d_T: array, + m: array, + D: array, + f_M: array, + rocd: array, + V_tas: array, + ) -> array: + # TODO: change to equation 3.2-1 """ - Total Energy Model. Speed and ROCD Controller. (BADA User Menu Equation 3.2-1c and 3.2-7) TODO: change to equation 3.2-1 + Total Energy Model. Speed and ROCD Controller. + (BADA User Menu Equation 3.2-1c and 3.2-7) Calculate thrust given ROCD and speed. Parameters @@ -825,9 +881,12 @@ def cal_tem_thrust(self, T, d_T, m, D, f_M, rocd, V_tas): Thr: float[] Thrust acting parallel to the aircraft velocity vector [N] """ - return rocd / f_M / ((T - d_T) / T) * m * self.__G_0 / V_tas + D + Thr = rocd / f_M / ((T - d_T) / T) * m * self.__G_0 / V_tas + D + return Thr - def cal_vs_accel(self, traffic, tas): + def cal_vs_accel( + self, traffic: "Traffic", tas: array + ) -> tuple[array, array]: """ Calculate vertical speed and acceleration given true airspeed. @@ -854,9 +913,9 @@ def cal_vs_accel(self, traffic, tas): traffic.weather.rho, traffic.configuration, self.perf_model.cal_expedite_descend_factor( - traffic.ap.expedite_descent + traffic.autopilot.expedite_descent ), - ) + ) # type: ignore self.thrust = self.perf_model.cal_thrust( traffic.vertical_mode, traffic.configuration, @@ -864,8 +923,8 @@ def cal_vs_accel(self, traffic, tas): traffic.tas, traffic.weather.d_T, self.drag, - traffic.ap.speed_mode, - ) + traffic.autopilot.speed_mode, + ) # type: ignore else: self.drag = np.array( [ @@ -878,7 +937,8 @@ def cal_vs_accel(self, traffic, tas): for i, x in enumerate(self.drag_model) ] ) - # drag.nonclean(mass=60000, tas=150, alt=100, flap_angle=20, path_angle=10, landing_gear=True) + # drag.nonclean(mass=60000, tas=150, alt=100, flap_angle=20, + # path_angle=10, landing_gear=True) self.thrust = np.array( [ x.cruise(tas=traffic.cas[i], alt=traffic.alt[i]) @@ -889,7 +949,10 @@ def cal_vs_accel(self, traffic, tas): for i, x in enumerate(self.thrust_model): if (traffic.vertical_mode[i] == VerticalMode.CLIMB) | ( (traffic.vertical_mode[i] == VerticalMode.LEVEL) - & (traffic.ap.speed_mode[i] == APSpeedMode.ACCELERATE) + & ( + traffic.autopilot.speed_mode[i] + == APSpeedMode.ACCELERATE + ) ): thrust.append( x.climb( @@ -897,29 +960,39 @@ def cal_vs_accel(self, traffic, tas): ) ) elif (traffic.vertical_mode[i] == VerticalMode.LEVEL) & ( - (traffic.ap.speed_mode[i] == APSpeedMode.CONSTANT_CAS) - | (traffic.ap.speed_mode[i] == APSpeedMode.CONSTANT_MACH) + ( + traffic.autopilot.speed_mode[i] + == APSpeedMode.CONSTANT_CAS + ) + | ( + traffic.autopilot.speed_mode[i] + == APSpeedMode.CONSTANT_MACH + ) ): thrust.append(self.drag[i]) elif (traffic.vertical_mode[i] == VerticalMode.DESCENT) | ( (traffic.vertical_mode[i] == VerticalMode.LEVEL) - & (traffic.ap.speed_mode[i] == APSpeedMode.DECELERATE) + & ( + traffic.autopilot.speed_mode[i] + == APSpeedMode.DECELERATE + ) ): thrust.append( x.descent_idle(tas=traffic.tas[i], alt=traffic.alt[i]) ) self.thrust = np.array(thrust) - # T = thrust.takeoff(tas=100, alt=0) T = thrust.climb(tas=200, alt=20000, roc=1000) + # T = thrust.takeoff(tas=100, alt=0) T = thrust.climb( + # tas=200, alt=20000, roc=1000) - # Total Energy Model + # total energy model energy share factor self.esf = self.cal_energy_share_factor( Unit.ft2m(traffic.alt), traffic.weather.T, traffic.weather.d_T, traffic.mach, - traffic.ap.speed_mode, + traffic.autopilot.speed_mode, traffic.vertical_mode, - ) # Energy share factor + ) # type: ignore if self.performance_mode == "BADA": rocd = self.cal_tem_rocd( traffic.weather.T, @@ -942,12 +1015,12 @@ def cal_vs_accel(self, traffic, tas): self.esf, self.thrust, tas, - 1.0, + np.ones_like(tas), ) accel = np.where( - (traffic.ap.speed_mode == APSpeedMode.ACCELERATE) - | (traffic.ap.speed_mode == APSpeedMode.DECELERATE), + (traffic.autopilot.speed_mode == APSpeedMode.ACCELERATE) + | (traffic.autopilot.speed_mode == APSpeedMode.DECELERATE), self.cal_tem_accel( traffic.weather.T, traffic.weather.d_T, @@ -962,7 +1035,9 @@ def cal_vs_accel(self, traffic, tas): return Unit.mps2ftpm(rocd), accel - def cal_fuel_burn(self, flight_phase, tas, alt): + def cal_fuel_burn( + self, flight_phase: uint_array, tas: array, alt: array + ) -> array: """ Calculate fuel burn @@ -972,7 +1047,7 @@ def cal_fuel_burn(self, flight_phase, tas, alt): Flight phase from Traffic class [Flight_phase enum] tas : float[] True airspeed [kt] - alt : _type_ + alt : float[] Altitude [ft] Returns @@ -985,16 +1060,18 @@ def cal_fuel_burn(self, flight_phase, tas, alt): flight_phase, tas, self.thrust, alt ) else: - return [ - x.at_thrust(acthr=self.thrust[i], alt=alt[i]) - for i, x in enumerate(self.fuel_flow_model) - ] + return np.array( + [ + x.at_thrust(acthr=self.thrust[i], alt=alt[i]) + for i, x in enumerate(self.fuel_flow_model) + ] + ) # FF = fuelflow.takeoff(tas=100, alt=0, throttle=1) # FF = fuelflow.enroute(mass=60000, tas=200, alt=20000, path_angle=3) # FF = fuelflow.enroute(mass=60000, tas=230, alt=32000, path_angle=0) - # ---------------------------- Turning ----------------------------------------- - def cal_rate_of_turn(self, bank_angle, V_tas): + ## TURNING + def cal_rate_of_turn(self, bank_angle: array, V_tas: array) -> array: """ Calculate rate of turn (Equation 5.3-1) @@ -1011,9 +1088,12 @@ def cal_rate_of_turn(self, bank_angle, V_tas): Rate of turn : float[] Rate of turn [deg/s] """ - return np.rad2deg(self.__G_0 / V_tas * np.tan(np.deg2rad(bank_angle))) + rate_of_turn = np.rad2deg( + self.__G_0 / V_tas * np.tan(np.deg2rad(bank_angle)) + ) + return rate_of_turn # type: ignore - def cal_bank_angle(self, rate_of_turn, V_tas): + def cal_bank_angle(self, rate_of_turn: array, V_tas: array) -> array: """ Calculate rate of turn (Equation 5.3-1) @@ -1030,11 +1110,12 @@ def cal_bank_angle(self, rate_of_turn, V_tas): bank_angle: float[] Bank angle [deg] """ - return np.rad2deg( + bank_angle = np.rad2deg( np.arctan(np.deg2rad(rate_of_turn) * V_tas / self.__G_0) ) + return bank_angle # type: ignore - def cal_turn_radius(self, bank_angle, V_tas): + def cal_turn_radius(self, bank_angle: array, V_tas: array) -> array: """ Calculate rate of turn (Equation 5.3-1) @@ -1051,9 +1132,12 @@ def cal_turn_radius(self, bank_angle, V_tas): turn_radius: float[] Turn radius [m] """ - return np.square(V_tas) / self.__G_0 / np.tan(np.deg2rad(bank_angle)) + turn_radius = ( + np.square(V_tas) / self.__G_0 / np.tan(np.deg2rad(bank_angle)) + ) + return turn_radius # type: ignore - def get_bank_angles(self, configuration): + def get_bank_angles(self, configuration: uint_array) -> array: """ Get standard nominal bank angles (Session 5.3) @@ -1071,8 +1155,9 @@ def get_bank_angles(self, configuration): return np.where( (configuration == Config.TAKEOFF) | (configuration == Config.LANDING), - self.perf_model._Bada__PHI_NORM_CIV_TOLD, - self.perf_model._Bada__PHI_NORM_CIV_OTHERS, + self.perf_model._Bada__PHI_NORM_CIV_TOLD, # type: ignore + self.perf_model._Bada__PHI_NORM_CIV_OTHERS, # type: ignore + # FIXME(abrah): why access private??? ) else: return np.where( @@ -1082,7 +1167,9 @@ def get_bank_angles(self, configuration): 30.0, ) - def update_configuration(self, V_cas, H_p, vertical_mode): + def update_configuration( + self, V_cas: array, H_p: array, vertical_mode: uint_array + ) -> uint_array: """ Update Flight Phase (section 3.5) diff --git a/airtrafficsim/core/traffic.py b/airtrafficsim/core/traffic.py index e16d342..cf0bed9 100644 --- a/airtrafficsim/core/traffic.py +++ b/airtrafficsim/core/traffic.py @@ -1,24 +1,37 @@ +from datetime import datetime +from typing import NewType + import numpy as np -from airtrafficsim.core.autopilot import Autopilot -from airtrafficsim.core.performance.performance import Performance -from airtrafficsim.core.weather.weather import Weather -from airtrafficsim.utils.calculation import Cal -from airtrafficsim.utils.enums import ( + +from ..utils.calculation import Cal +from ..utils.enums import ( APSpeedMode, Config, FlightPhase, SpeedMode, VerticalMode, ) -from airtrafficsim.utils.unit_conversion import Unit +from ..utils.unit_conversion import Unit +from .autopilot import Autopilot +from .performance.performance import Performance, PerformanceMode +from .weather.weather import Weather, WeatherMode + +AircraftIdx = NewType("AircraftIdx", int) +"""Index of the aircraft in the arrays""" class Traffic: def __init__( - self, file_name, start_time, end_time, weather_mode, performance_mode - ): + self, + file_name: str, + start_time: datetime, + duration_s: float, + weather_mode: WeatherMode, + performance_mode: PerformanceMode, + ) -> None: """ - Initialize base traffic array to store aircraft state variables for one timestep. + Initialize base traffic array to store aircraft state variables for one + timestep. Parameters ---------- @@ -32,35 +45,83 @@ def __init__( # self.N = N # """Maximum aircraft count""" - # self.df = np.empty([0], dtype=[ - # ('index', 'i8'), ('callsign', 'U10'), ('type', 'U4'), ('configuration', 'i8'), ('flight_phase', 'i8'), - # ('lat', 'f8'), ('long', 'f8'), ('alt', 'f8'), ('trans_alt', 'f8'), - # ('heading', 'f8'), ('track_angle', 'f8'), ('bank_angle', 'f8'), ('path_angle', 'f8'), - # ('cas', 'f8'), ('tas', 'f8'), ('gs_north', 'f8'), ('gs_east', 'f8'), ('mach', 'f8'), ('accel', 'f8'), ('speed_mode', 'i8'), - # ('max_alt', 'f8'), ('max_cas', 'f8'), ('max_mach', 'f8'), - # ('vs', 'f8'), ('fpa', 'f8'), ('vertical_mode', 'i8'), - # ('mass', 'f8'), ('empty_weight', 'f8'), ('fuel_weight', 'f8'), ('payload_weight', 'f8'), ('fuel_consumed', 'f8'), - # # Autopilot - # ('ap_alt', 'f8'), ('ap_heading', 'f8'), ('ap_track_angle', 'f8'), ('ap_rate_of_turn', 'f8'), - # ('ap_cas', 'f8'), ('ap_mach', 'f8'), ('ap_vs', 'f8'), ('ap_fpa', 'f8'), - # ('ap_lat', 'f8'), ('ap_long', 'f8'), ('ap_lat_next', 'f8'), ('ap_long_next', 'f8'), ('ap_hv_next_wp', '?'), ('ap_dist_wp', 'f8') - # ('ap_flight_plan_index', 'i8'), ('ap_procedure_speed', 'f8') - # # Weather - # ('wind_speed', 'f8'), ('wind_dir', 'f8'), ('wind_north', 'f8'), ('wind_east', 'f8'), - # ('d_T', 'f8'), ('d_p', 'f8'), ('T', 'f8'), ('p', 'f8'), ('rho', 'f8') - # ]) + # FIXME(abrah): consider using pl.DataFrame if we really want to use SoA + # self.df = np.empty( + # [0], + # dtype=[ + # ("index", "i8"), + # ("callsign", "U10"), + # ("type", "U4"), + # ("configuration", "i8"), + # ("flight_phase", "i8"), + # ("lat", "f8"), + # ("long", "f8"), + # ("alt", "f8"), + # ("trans_alt", "f8"), + # ("heading", "f8"), + # ("track_angle", "f8"), + # ("bank_angle", "f8"), + # ("path_angle", "f8"), + # ("cas", "f8"), + # ("tas", "f8"), + # ("gs_north", "f8"), + # ("gs_east", "f8"), + # ("mach", "f8"), + # ("accel", "f8"), + # ("speed_mode", "i8"), + # ("max_alt", "f8"), + # ("max_cas", "f8"), + # ("max_mach", "f8"), + # ("vs", "f8"), + # ("fpa", "f8"), + # ("vertical_mode", "i8"), + # ("mass", "f8"), + # ("empty_weight", "f8"), + # ("fuel_weight", "f8"), + # ("payload_weight", "f8"), + # ("fuel_consumed", "f8"), + # # Autopilot + # ("ap_alt", "f8"), + # ("ap_heading", "f8"), + # ("ap_track_angle", "f8"), + # ("ap_rate_of_turn", "f8"), + # ("ap_cas", "f8"), + # ("ap_mach", "f8"), + # ("ap_vs", "f8"), + # ("ap_fpa", "f8"), + # ("ap_lat", "f8"), + # ("ap_long", "f8"), + # ("ap_lat_next", "f8"), + # ("ap_long_next", "f8"), + # ("ap_hv_next_wp", "?"), + # ("ap_dist_wp", "f8")("ap_flight_plan_index", "i8"), + # ("ap_procedure_speed", "f8") + # # Weather + # ("wind_speed", "f8"), + # ("wind_dir", "f8"), + # ("wind_north", "f8"), + # ("wind_east", "f8"), + # ("d_T", "f8"), + # ("d_p", "f8"), + # ("T", "f8"), + # ("p", "f8"), + # ("rho", "f8"), + # ], + # ) self.index = np.zeros([0]) - """Index array to indicate whether there is an aircraft active in each index.""" + """Index array to indicate whether there is an aircraft active in each + index.""" # General information self.call_sign = np.empty([0], dtype="U10") """Callsign [string]""" self.aircraft_type = np.empty([0], dtype="U4") """Aircraft type in ICAO format [string]""" - self.configuration = np.zeros([0]) - """Aircraft configuration [Configuration enum 1: Clean, 2: Take Off, 3: Approach, 4: Landing]""" - self.flight_phase = np.zeros([0]) + self.configuration = np.zeros([0], dtype=np.uint16) + """Aircraft configuration [Configuration enum 1: Clean, 2: Take Off, + 3: Approach, 4: Landing]""" + self.flight_phase = np.zeros([0], dtype=np.uint16) """Flight phase [Flight_phase enum] (BADA section 3.5)""" # Position @@ -94,7 +155,7 @@ def __init__( """Ground speed - North[knot]""" self.gs_east = np.zeros([0]) """Ground speed - East [knot] """ - self.mach = np.zeros([0]) + self.mach = np.zeros([0], dtype=np.float64) """Mach number [dimensionless]""" self.accel = np.zeros([0]) """Acceleration [m/s^2]""" @@ -114,7 +175,7 @@ def __init__( """Vertical speed [feet/min]""" self.fpa = np.zeros([0]) """Flight path angle [deg]""" - self.vertical_mode = np.zeros([0]) + self.vertical_mode = np.zeros([0], dtype=np.uint16) """Vertical mode [Vertical mode enum 1: LEVEL, 2: CLIMB, 3: DESCENT]""" # Weight and balance @@ -132,34 +193,35 @@ def __init__( # Sub classes self.perf = Performance(performance_mode) """Performance class""" - self.ap = Autopilot() + self.autopilot = Autopilot() """Autopilot class""" - self.weather = Weather(start_time, end_time, weather_mode, file_name) + self.weather = Weather(start_time, duration_s, weather_mode, file_name) """Weather class""" + # FIXME(abrah): this is doing way too much stuff. def add_aircraft( self, - call_sign, - aircraft_type, - flight_phase, - configuration, - lat, - long, - alt, - heading, - cas, - fuel_weight, - payload_weight, - departure_airport, - departure_runway, - sid, - arrival_airport, - arrival_runway, - star, - approach, - flight_plan, - cruise_alt, - ): + call_sign: str, + aircraft_type: str, + flight_phase: FlightPhase, + configuration: Config, + lat: float, + long: float, + alt: float, + heading: float, + cas: float, + fuel_weight: float, + payload_weight: float, + departure_airport: str, + departure_runway: str, + sid: str, + arrival_airport: str, + arrival_runway: str, + star: str, + approach: str, + flight_plan: list[str], + cruise_alt: float, + ) -> AircraftIdx: """ Add an aircraft to traffic array. @@ -169,12 +231,13 @@ def add_aircraft( Index of the added aircraft """ + # FIXME: use logger. print("Traffic.py - add_aircraft()", call_sign, " Type:", aircraft_type) # Add aircraft in performance, weather, and autopilot array self.perf.add_aircraft(aircraft_type) self.weather.add_aircraft(alt, self.perf) - self.ap.add_aircraft( + self.autopilot.add_aircraft( lat, long, alt, @@ -209,7 +272,9 @@ def add_aircraft( self.tas, Unit.mps2kts( self.perf.cas_to_tas( - Unit.kts2mps(cas), self.weather.p[-1], self.weather.rho[-1] + Unit.kts2mps(cas), + self.weather.p[-1], + self.weather.rho[-1], ) ), ) @@ -246,15 +311,15 @@ def add_aircraft( Unit.m2ft(self.perf.cal_transition_alt(-1, self.weather.d_T[-1])), ) - self.max_alt = self.perf.cal_maximum_alt(self.weather.d_T, self.mass) - self.max_cas, self.max_mach = self.perf.cal_maximum_speed() + self.max_alt = self.perf.cal_maximum_alt(self.weather.d_T, self.mass) # type: ignore + self.max_cas, self.max_mach = self.perf.cal_maximum_speed() # type: ignore # Increase aircraft count self.n = self.n + 1 - return self.n - 1 + return AircraftIdx(self.n - 1) - def del_aircraft(self, index): + def del_aircraft(self, index: AircraftIdx) -> None: """ Delete an aircraft from traffic array. @@ -299,44 +364,50 @@ def del_aircraft(self, index): self.fuel_consumed = np.delete(self.fuel_consumed, i) self.perf.del_aircraft(i) - self.ap.del_aircraft(i) + self.autopilot.del_aircraft(i) self.weather.del_aircraft(i) - def update(self, global_time, d_t=1): + def update(self, seconds_since_start: float) -> None: """ Update aircraft state for each timestep given ATC/autopilot command. - - Parameters - ---------- - d_t: float - delta time per timestep [s] TODO: need? """ # Update atmosphere self.weather.update( - self.lat, self.long, self.alt, self.perf, global_time + self.lat, self.long, self.alt, self.perf, seconds_since_start ) # Ceiling # min_speed = self.perf.cal_minimum_speed(self.flight_phase) # max_d_tas = self.perf.cal_max_d_tas(d_t) - # max_d_rocd = self.perf.cal_max_d_rocd(d_t, self.unit.knots_to_mps(self.d_cas), tas, self.unit.ftpm_to_mps(self.vs)) + # max_d_rocd = self.perf.cal_max_d_rocd(d_t, + # self.unit.knots_to_mps(self.d_cas), tas, + # self.unit.ftpm_to_mps(self.vs)) self.speed_mode = np.where( self.alt < self.trans_alt, SpeedMode.CAS, SpeedMode.MACH ) # Update autopilot - self.ap.update(self) + self.autopilot.update(self) # Flight phase and configuration # Take off -> climb - # self.flight_phase = np.where((self.flight_phase == Flight_phase.TAKEOFF) & (self.alt > 1500.0), Flight_phase.CLIMB, self.flight_phase) - # self.flight_phase = np.where((self.flight_phase != Flight_phase.TAKEOFF) & (self.vertical_mode == Vertical_mode.CLIMB), Flight_phase.CLIMB, self.flight_phase) + # self.flight_phase = np.where( + # (self.flight_phase == Flight_phase.TAKEOFF) & + # (self.alt > 1500.0), Flight_phase.CLIMB, self.flight_phase) + # self.flight_phase = np.where( + # (self.flight_phase != Flight_phase.TAKEOFF) & + # (self.vertical_mode == Vertical_mode.CLIMB), + # Flight_phase.CLIMB, self.flight_phase) # # Climb -> Cruise - # self.flight_phase = np.where(self.vertical_mode == Vertical_mode.LEVEL, Flight_phase.CRUISE, self.flight_phase) #TODO: Or use cruise altitude? + # self.flight_phase = np.where( + # self.vertical_mode == Vertical_mode.LEVEL, + # Flight_phase.CRUISE, self.flight_phase) #TODO: Or use cruise altitude? # # Cruise-Descent - # self.flight_phase = np.where(self.vertical_mode == Vertical_mode.DESCENT, Flight_phase.DESCENT, self.flight_phase) + # self.flight_phase = np.where( + # self.vertical_mode ==Vertical_mode.DESCENT, + # Flight_phase.DESCENT, self.flight_phase) self.configuration = self.perf.update_configuration( self.cas, self.alt, self.vertical_mode ) @@ -369,7 +440,7 @@ def update(self, global_time, d_t=1): ) # Bank angle - d_heading = Cal.cal_angle_diff(self.heading, self.ap.heading) + d_heading = Cal.cal_angle_diff(self.heading, self.autopilot.heading) self.bank_angle = np.select( condlist=[ d_heading > 0.5, @@ -385,30 +456,39 @@ def update(self, global_time, d_t=1): ) tas = Unit.kts2mps(self.tas) # TAS in m/s - self.vs, self.accel = self.perf.cal_vs_accel(self, tas) + self.vs, self.accel = self.perf.cal_vs_accel(self, tas) # type: ignore # Air Speed - # self.tas = self.perf.cas_to_tas(self.cas, self.weather.p, self.weather.rho) + # self.tas = self.perf.cas_to_tas(self.cas, self.weather.p, + # self.weather.rho) tas = tas + self.accel - self.mach = self.perf.tas_to_mach(tas, self.weather.T) + self.mach = self.perf.tas_to_mach(tas, self.weather.T) # type: ignore self.cas = Unit.mps2kts( self.perf.tas_to_cas(tas, self.weather.p, self.weather.rho) - ) + ) # type: ignore # Bound to autopilot self.mach = np.select( condlist=[ (self.speed_mode == SpeedMode.MACH) - & (self.ap.speed_mode == APSpeedMode.ACCELERATE), + & (self.autopilot.speed_mode == APSpeedMode.ACCELERATE), (self.speed_mode == SpeedMode.MACH) - & (self.ap.speed_mode == APSpeedMode.DECELERATE), + & (self.autopilot.speed_mode == APSpeedMode.DECELERATE), (self.speed_mode == SpeedMode.MACH) - & (self.ap.speed_mode == APSpeedMode.CONSTANT_MACH), + & (self.autopilot.speed_mode == APSpeedMode.CONSTANT_MACH), ], choicelist=[ - np.where(self.mach > self.ap.mach, self.ap.mach, self.mach), - np.where(self.mach < self.ap.mach, self.ap.mach, self.mach), - self.ap.mach, + np.where( + self.mach > self.autopilot.mach, + self.autopilot.mach, + self.mach, + ), + np.where( + self.mach < self.autopilot.mach, + self.autopilot.mach, + self.mach, + ), + self.autopilot.mach, ], default=self.mach, ) @@ -416,26 +496,28 @@ def update(self, global_time, d_t=1): self.cas = np.select( condlist=[ (self.speed_mode == SpeedMode.CAS) - & (self.ap.speed_mode == APSpeedMode.ACCELERATE), + & (self.autopilot.speed_mode == APSpeedMode.ACCELERATE), (self.speed_mode == SpeedMode.CAS) - & (self.ap.speed_mode == APSpeedMode.DECELERATE), + & (self.autopilot.speed_mode == APSpeedMode.DECELERATE), (self.speed_mode == SpeedMode.CAS) - & (self.ap.speed_mode == APSpeedMode.CONSTANT_CAS), + & (self.autopilot.speed_mode == APSpeedMode.CONSTANT_CAS), ], choicelist=[ np.where( - self.cas > self.ap.cas, self.ap.cas, self.cas + self.cas > self.autopilot.cas, self.autopilot.cas, self.cas ), # TODO: change to minimum - np.where(self.cas < self.ap.cas, self.ap.cas, self.cas), - self.ap.cas, + np.where( + self.cas < self.autopilot.cas, self.autopilot.cas, self.cas + ), + self.autopilot.cas, ], default=self.cas, ) tas = np.select( condlist=[ - self.ap.speed_mode == APSpeedMode.CONSTANT_MACH, - self.ap.speed_mode == APSpeedMode.CONSTANT_CAS, + self.autopilot.speed_mode == APSpeedMode.CONSTANT_MACH, + self.autopilot.speed_mode == APSpeedMode.CONSTANT_CAS, ], choicelist=[ self.perf.mach_to_tas(self.mach, self.weather.T), @@ -450,16 +532,16 @@ def update(self, global_time, d_t=1): condlist=[ (self.speed_mode == SpeedMode.CAS) & ( - (self.ap.speed_mode == APSpeedMode.ACCELERATE) - | (self.ap.speed_mode == APSpeedMode.DECELERATE) + (self.autopilot.speed_mode == APSpeedMode.ACCELERATE) + | (self.autopilot.speed_mode == APSpeedMode.DECELERATE) ) - & (self.cas == self.ap.cas), + & (self.cas == self.autopilot.cas), (self.speed_mode == SpeedMode.MACH) & ( - (self.ap.speed_mode == APSpeedMode.ACCELERATE) - | (self.ap.speed_mode == APSpeedMode.DECELERATE) + (self.autopilot.speed_mode == APSpeedMode.ACCELERATE) + | (self.autopilot.speed_mode == APSpeedMode.DECELERATE) ) - & (self.mach == self.ap.mach), + & (self.mach == self.autopilot.mach), ], choicelist=[ self.perf.cas_to_tas( @@ -473,10 +555,10 @@ def update(self, global_time, d_t=1): self.mach = np.where( (self.speed_mode == SpeedMode.CAS) & ( - (self.ap.speed_mode == APSpeedMode.ACCELERATE) - | (self.ap.speed_mode == APSpeedMode.DECELERATE) + (self.autopilot.speed_mode == APSpeedMode.ACCELERATE) + | (self.autopilot.speed_mode == APSpeedMode.DECELERATE) ) - & (self.cas == self.ap.cas), + & (self.cas == self.autopilot.cas), self.perf.tas_to_mach(tas, self.weather.T), self.mach, ) @@ -484,17 +566,17 @@ def update(self, global_time, d_t=1): self.cas = np.where( (self.speed_mode == SpeedMode.MACH) & ( - (self.ap.speed_mode == APSpeedMode.ACCELERATE) - | (self.ap.speed_mode == APSpeedMode.DECELERATE) + (self.autopilot.speed_mode == APSpeedMode.ACCELERATE) + | (self.autopilot.speed_mode == APSpeedMode.DECELERATE) ) - & (self.mach == self.ap.mach), + & (self.mach == self.autopilot.mach), Unit.mps2kts( self.perf.tas_to_cas(tas, self.weather.p, self.weather.rho) ), self.cas, ) - self.tas = Unit.mps2kts(tas) + self.tas = Unit.mps2kts(tas) # type: ignore # Heading # TODO: https://skybrary.aero/articles/rate-turn @@ -502,7 +584,7 @@ def update(self, global_time, d_t=1): self.heading = np.where( (np.abs(d_heading) < np.abs(rate_of_turn)) | (np.abs(d_heading) < 0.5), - self.ap.heading, + self.autopilot.heading, self.heading + rate_of_turn, ) self.heading = np.select( @@ -534,8 +616,12 @@ def update(self, global_time, d_t=1): self.vertical_mode == VerticalMode.DESCENT, ], choicelist=[ - np.where(self.alt > self.ap.alt, self.ap.alt, self.alt), - np.where(self.alt < self.ap.alt, self.ap.alt, self.alt), + np.where( + self.alt > self.autopilot.alt, self.autopilot.alt, self.alt + ), + np.where( + self.alt < self.autopilot.alt, self.autopilot.alt, self.alt + ), ], default=self.alt, ) @@ -544,5 +630,5 @@ def update(self, global_time, d_t=1): fuel_burn = self.perf.cal_fuel_burn( self.configuration, self.tas, self.alt ) - self.fuel_consumed = self.fuel_consumed + fuel_burn - self.mass = self.mass - fuel_burn + self.fuel_consumed = self.fuel_consumed + fuel_burn # type: ignore + self.mass = self.mass - fuel_burn # type: ignore diff --git a/airtrafficsim/core/weather/era5.py b/airtrafficsim/core/weather/era5.py index 8fd17cd..97d5c8b 100644 --- a/airtrafficsim/core/weather/era5.py +++ b/airtrafficsim/core/weather/era5.py @@ -1,6 +1,7 @@ -from datetime import time, timedelta +from datetime import datetime, time, timedelta from pathlib import Path +# NOTE(abrah): consider using google arco instead import cdsapi @@ -9,8 +10,11 @@ class Era5: A utility class to handle ERA5 weather data """ + # FIXME(abrah): rename end_time. @staticmethod - def download_data(start_time, end_time, file_name): + def download_data( + start_time: datetime, duration_s: float, file_name: str + ) -> tuple[Path, Path]: """ Download ERA5 data @@ -26,11 +30,11 @@ def download_data(start_time, end_time, file_name): Returns ------- path: Path - Path to the downloaded weather data + Path to the multi-level and surface ERA5 data """ c = cdsapi.Client() if Path(__file__).parent.parent.parent.resolve().joinpath( - "data/weather/era5/" + file_name + "data/weather/era5/" + file_name # FIXME(abrah): injection attack. ).exists() and any( Path(__file__) .parent.parent.parent.resolve() @@ -41,7 +45,9 @@ def download_data(start_time, end_time, file_name): else: print("Downloading ERA5 data.") print( - "Download expected to complete in few minutes. Visit https://cds.climate.copernicus.eu/cdsapp#!/yourrequests for the status of the request. \n" + "Download expected to complete in few minutes." + "Visit https://cds.climate.copernicus.eu/cdsapp#!/yourrequests " + "for the status of the request. \n" ) if ( not Path(__file__) @@ -53,11 +59,11 @@ def download_data(start_time, end_time, file_name): "data/weather/era5/" + file_name ).mkdir() tmp = start_time - year = [] - month = [] - day = [] - hour = [] - while tmp < (start_time + timedelta(seconds=end_time)): + year: list[str] = [] + month: list[str] = [] + day: list[str] = [] + hour: list[str] = [] + while tmp < (start_time + timedelta(seconds=duration_s)): if not year or tmp.strftime("%Y") != year[-1]: year.append(tmp.strftime("%Y")) if not month or tmp.strftime("%m") != month[-1]: diff --git a/airtrafficsim/core/weather/weather.py b/airtrafficsim/core/weather/weather.py index bf5b44f..e2dfc41 100644 --- a/airtrafficsim/core/weather/weather.py +++ b/airtrafficsim/core/weather/weather.py @@ -1,19 +1,27 @@ -from datetime import timedelta +from datetime import datetime, timedelta +from typing import Literal, TypeAlias import xarray as xr import numpy as np -from airtrafficsim.core.performance.performance import Performance -from airtrafficsim.core.weather.era5 import Era5 -from airtrafficsim.utils.unit_conversion import Unit +from ...types import array +from ...utils.unit_conversion import Unit +from ..performance.performance import Performance +from ..weather.era5 import Era5 + +WeatherMode: TypeAlias = Literal["ISA", "ERA5", ""] +# TODO(abrah): repalce with str | None instead of this. -class Weather: - """ - Weather class - """ - def __init__(self, start_time, end_time, weather_mode, file_name): +class Weather: + def __init__( + self, + start_time: datetime, + duration_s: float, + weather_mode: WeatherMode, + file_name: str, + ) -> None: """ Weather class constructor @@ -21,14 +29,14 @@ def __init__(self, start_time, end_time, weather_mode, file_name): ---------- start_time : datetime Start time of the simulation - end_time : datetime - End time of the simulation + duration : float + Duration of the simulation weather_mode : str Weather mode [ISA, ERA5] file_name : str File name of the weather data """ - self.mode = weather_mode + self.weather_mode = weather_mode """Weather mode [ISA, ERA5]""" self.start_time = start_time """Start time of the simulation [datetime]""" @@ -43,6 +51,7 @@ def __init__(self, start_time, end_time, weather_mode, file_name): self.wind_east = np.zeros([0]) """Wind - East [knots]""" + # FIXME(abrah): this is duplicated in ..performance. remove this. # Atmospheric condition self.d_T = np.zeros([0]) """Temperature difference compare to ISA [K]""" @@ -56,14 +65,14 @@ def __init__(self, start_time, end_time, weather_mode, file_name): """Density [kg/m^3]""" # Download ERA5 data - if self.mode == "ERA5": + if self.weather_mode == "ERA5": multilevel, surface = Era5.download_data( - start_time, end_time, file_name + start_time, duration_s, file_name ) self.weather_data = xr.open_dataset(multilevel) self.radar_data = xr.open_dataset(surface) - def add_aircraft(self, alt, perf: Performance): + def add_aircraft(self, alt: float, perf: Performance) -> None: """ Add aircraft to the weather class @@ -91,7 +100,7 @@ def add_aircraft(self, alt, perf: Performance): self.rho, perf.cal_air_density(self.p[-1], self.T[-1]) ) - def del_aircraft(self, index): + def del_aircraft(self, index: int) -> None: """ Delete aircraft from the weather class @@ -110,7 +119,14 @@ def del_aircraft(self, index): self.p = np.delete(self.p, index) self.rho = np.delete(self.rho, index) - def update(self, lat, long, alt, perf: Performance, global_time): + def update( + self, + lat: array, + long: array, + alt: array, + perf: Performance, + seconds_since_start: float, + ) -> None: """ Update weather data @@ -124,17 +140,17 @@ def update(self, lat, long, alt, perf: Performance, global_time): Altitude of the aircraft [ft] perf : Performance Performance class - global_time : float + seconds_since_start : float Time since the start of the simulation [seconds] """ - if self.mode == "ERA5": + if self.weather_mode == "ERA5": ds = self.weather_data.sel( longitude=xr.DataArray(long, dims="points"), latitude=xr.DataArray(lat, dims="points"), time=np.datetime64( - (self.start_time + timedelta(seconds=global_time)).replace( - second=0, minute=0 - ), + ( + self.start_time + timedelta(seconds=seconds_since_start) + ).replace(second=0, minute=0), "ns", ), method="ffill", @@ -143,7 +159,9 @@ def update(self, lat, long, alt, perf: Performance, global_time): np.array( [ np.searchsorted( - -x, -Unit.ft2m(alt) * 9.80665, side="right" + -x, + -Unit.ft2m(alt) * 9.80665, # type: ignore + side="right", ) for x, alt in zip(ds["z"].values.T, alt) ] @@ -151,14 +169,16 @@ def update(self, lat, long, alt, perf: Performance, global_time): - 1 ) temp = np.array([x[i] for x, i in zip(ds["t"].values.T, index)]) - self.d_T = temp - perf.cal_temperature(Unit.ft2m(alt), 0.0) + self.d_T = temp - perf.cal_temperature( + Unit.ft2m(alt), np.array(0.0) + ) # type: ignore self.wind_east = Unit.mps2kts( np.array([x[i] for x, i in zip(ds["u"].values.T, index)]) - ) + ) # type: ignore self.wind_north = Unit.mps2kts( np.array([x[i] for x, i in zip(ds["v"].values.T, index)]) - ) + ) # type: ignore - self.T = perf.cal_temperature(Unit.ft2m(alt), self.d_T) - self.p = perf.cal_air_pressure(Unit.ft2m(alt), self.T, self.d_T) - self.rho = perf.cal_air_density(self.p, self.T) + self.T = perf.cal_temperature(Unit.ft2m(alt), self.d_T) # type: ignore + self.p = perf.cal_air_pressure(Unit.ft2m(alt), self.T, self.d_T) # type: ignore + self.rho = perf.cal_air_density(self.p, self.T) # type: ignore diff --git a/airtrafficsim/data/environment/ConvertHistoricDemo.py b/airtrafficsim/data/environment/ConvertHistoricDemo.py index 23c8199..9709df2 100644 --- a/airtrafficsim/data/environment/ConvertHistoricDemo.py +++ b/airtrafficsim/data/environment/ConvertHistoricDemo.py @@ -1,5 +1,6 @@ from datetime import datetime, timedelta from pathlib import Path +from typing import TypeAlias import numpy as np import pandas as pd @@ -14,16 +15,18 @@ rdp, ) +Callsign: TypeAlias = str + class ConvertHistoricDemo(Environment): - def __init__(self): + def __init__(self) -> None: # Initialize environment super class super().__init__( file_name=Path(__file__).name.removesuffix( ".py" ), # File name (do not change) start_time=datetime.fromisoformat("2018-05-01T00:00:00+00:00"), - end_time=3600 * 2, + duration_s=3600 * 2, weather_mode="", performance_mode="BADA", ) @@ -45,7 +48,7 @@ def __init__(self): ) # Storage for historic aircraft data - self.call_sign = [] + self.call_sign: list[Callsign] = [] self.type = [] self.star = [] self.approach = [] @@ -53,8 +56,8 @@ def __init__(self): self.speed = [] self.start_alt = [] self.heading = [] - self.time = [] - self.aircraft_list = {} + timestamps = [] + self.aircraft_list: dict[Callsign, Aircraft] = {} # Loop all historic data files for file in self.historic_data_path.iterdir(): @@ -78,9 +81,13 @@ def __init__(self): self.approach.append(approach_result) # Determine aircraft appearance point (150km to hong kong) + num_points = traj.shape[0] index = np.where( Cal.cal_great_circle_dist( - traj[:, 0], traj[:, 1], 22.3193, 114.1694 + traj[:, 0], + traj[:, 1], + np.full((num_points,), 22.3193), + np.full((num_points,), 114.1694), ) < 200 )[0][0] @@ -88,7 +95,7 @@ def __init__(self): self.speed.append(df["gspeed"].iloc[index]) self.start_alt.append(df["alt"].iloc[index]) self.heading.append(df["hangle"].iloc[index]) - self.time.append(df["timestamp"].iloc[index]) + timestamps.append(df["timestamp"].iloc[index]) print( file.name.removesuffix(".csv"), arrival_result, approach_result @@ -96,17 +103,15 @@ def __init__(self): print("Finished analyzing data") - # Get starting time - self.time = np.array(self.time) + self.timestamps = np.array(timestamps) - def should_end(self): + def should_end(self) -> bool: return False - def atc_command(self): + def atc_command(self) -> None: # Handle creation and deletion of aircraft - time = self.start_time + timedelta(seconds=self.global_time) - time = int(time.timestamp()) - index = np.where(self.time == time)[0] + time = self.start_time + timedelta(seconds=self.seconds_since_start) + index = np.where(self.timestamps == int(time.timestamp()))[0] # Add aircraft for i in index: self.aircraft_list[self.call_sign[i]] = Aircraft( @@ -129,7 +134,7 @@ def atc_command(self): cruise_alt=37000, ) # Delete aircraft - index = self.traffic.index[self.traffic.ap.hv_next_wp is False] + index = self.traffic.index[self.traffic.autopilot.hv_next_wp is False] for i in index: self.traffic.del_aircraft(i) @@ -137,5 +142,5 @@ def atc_command(self): # Holding and vectoring if "5J150" in self.aircraft_list: # self.aircraft_list["5J150"].set_vectoring(60, 195, "GUAVA") - if self.global_time == 1600: + if self.seconds_since_start == 1600: self.aircraft_list["5J150"].set_holding(2, "BETTY", "VH") diff --git a/airtrafficsim/data/environment/DemoEnv.py b/airtrafficsim/data/environment/DemoEnv.py index 94b3ddb..fc718d0 100644 --- a/airtrafficsim/data/environment/DemoEnv.py +++ b/airtrafficsim/data/environment/DemoEnv.py @@ -7,14 +7,14 @@ class DemoEnv(Environment): - def __init__(self): + def __init__(self) -> None: # Initialize environment super class super().__init__( file_name=Path(__file__).name.removesuffix( ".py" ), # File name (do not change) start_time=datetime.fromisoformat("2022-03-22T00:00:00+00:00"), - end_time=1000, + duration_s=1000, weather_mode="", performance_mode="BADA", ) @@ -39,7 +39,6 @@ def __init__(self): approach="I07R", cruise_alt=37000, ) - # self.aircraft_head.set_speed(250.0) # To set the aircraft to follow given speed command instead of auto procedural self.aircraft_fol = Aircraft( self.traffic, call_sign="FOLLOW", @@ -56,22 +55,22 @@ def __init__(self): cruise_alt=37000, ) - def should_end(self): + def should_end(self) -> bool: return False - def atc_command(self): + def atc_command(self) -> None: # User algorithm - if self.global_time == 10: + if self.seconds_since_start == 10: # Right self.aircraft_fol.set_heading(220) # Left # self.aircraft_head.set_heading(150) - if self.global_time == 300: + if self.seconds_since_start == 300: # Climb self.aircraft_fol.set_alt(30000) # Descend # self.aircraft_head.set_alt(11000) - if self.global_time == 900: + if self.seconds_since_start == 900: self.traffic.del_aircraft(self.aircraft_head.index) diff --git a/airtrafficsim/data/environment/FullFlightDemo.py b/airtrafficsim/data/environment/FullFlightDemo.py index c70c08e..8beeaed 100644 --- a/airtrafficsim/data/environment/FullFlightDemo.py +++ b/airtrafficsim/data/environment/FullFlightDemo.py @@ -8,14 +8,14 @@ class FullFlightDemo(Environment): - def __init__(self): + def __init__(self) -> None: # Initialize environment super class super().__init__( file_name=Path(__file__).name.removesuffix( ".py" ), # File name (do not change) start_time=datetime.fromisoformat("2022-03-22T00:00:00+00:00"), - end_time=7000, + duration_s=7000, weather_mode="", performance_mode="BADA", ) @@ -46,12 +46,12 @@ def __init__(self): cruise_alt=37000, ) - def should_end(self): + def should_end(self) -> bool: # if (self.global_time > 60 and np.all((self.traffic.alt == 0))): # return True # else: return False - def atc_command(self): + def atc_command(self) -> None: # User algorithm pass diff --git a/airtrafficsim/data/environment/OpenApDemo.py b/airtrafficsim/data/environment/OpenApDemo.py index dfc01bb..9882ad8 100644 --- a/airtrafficsim/data/environment/OpenApDemo.py +++ b/airtrafficsim/data/environment/OpenApDemo.py @@ -7,14 +7,14 @@ class OpenApDemo(Environment): - def __init__(self): + def __init__(self) -> None: # Initialize environment super class super().__init__( file_name=Path(__file__).name.removesuffix( ".py" ), # File name (do not change) start_time=datetime.fromisoformat("2022-03-22T00:00:00+00:00"), - end_time=1000, + duration_s=1000, weather_mode="", performance_mode="OpenAP", ) @@ -50,29 +50,26 @@ def __init__(self): payload_weight=12000.0, cruise_alt=37000, ) - self.aircraft_head.set_speed( - 250.0 - ) # To set the aircraft to follow given speed command instead of auto procedural - self.aircraft_fol.set_speed( - 310.0 - ) # To set the aircraft to follow given speed command instead of auto procedural + # follow given speed command instead of auto procedural + self.aircraft_head.set_speed(250.0) + self.aircraft_fol.set_speed(310.0) - def should_end(self): + def should_end(self) -> bool: return False - def atc_command(self): + def atc_command(self) -> None: # User algorithm - if self.global_time == 100: + if self.seconds_since_start == 100: # Right self.aircraft_fol.set_heading(220) # Left self.aircraft_head.set_heading(150) - if self.global_time == 500: + if self.seconds_since_start == 500: # Climb self.aircraft_fol.set_alt(30000) # Descend self.aircraft_head.set_alt(11000) - if self.global_time == 900: + if self.seconds_since_start == 900: self.traffic.del_aircraft(self.aircraft_head.index) diff --git a/airtrafficsim/data/environment/WeatherDemo.py b/airtrafficsim/data/environment/WeatherDemo.py index 73e8e63..1a0ab4c 100644 --- a/airtrafficsim/data/environment/WeatherDemo.py +++ b/airtrafficsim/data/environment/WeatherDemo.py @@ -7,14 +7,14 @@ class WeatherDemo(Environment): - def __init__(self): + def __init__(self) -> None: # Initialize environment super class super().__init__( file_name=Path(__file__).name.removesuffix( ".py" ), # File name (do not change) start_time=datetime.fromisoformat("2018-05-01T00:00:00+00:00"), - end_time=1000, + duration_s=1000, weather_mode="ERA5", performance_mode="BADA", ) @@ -39,7 +39,8 @@ def __init__(self): approach="I07R", cruise_alt=37000, ) - # self.aircraft_head.set_speed(250.0) # To set the aircraft to follow given speed command instead of auto procedural + # self.aircraft_head.set_speed(250.0)# To set the aircraft to + # follow given speed command instead of auto procedural self.aircraft_fol = Aircraft( self.traffic, call_sign="FOLLOW", @@ -56,22 +57,22 @@ def __init__(self): cruise_alt=37000, ) - def should_end(self): + def should_end(self) -> bool: return False - def atc_command(self): + def atc_command(self) -> None: # User algorithm - if self.global_time == 10: + if self.seconds_since_start == 10: # Right self.aircraft_fol.set_heading(220) # Left # self.aircraft_head.set_heading(150) - if self.global_time == 300: + if self.seconds_since_start == 300: # Climb self.aircraft_fol.set_alt(30000) # Descend # self.aircraft_head.set_alt(11000) - if self.global_time == 900: + if self.seconds_since_start == 900: self.traffic.del_aircraft(self.aircraft_head.index) diff --git a/airtrafficsim/geometry.py b/airtrafficsim/geometry.py new file mode 100644 index 0000000..6a7f734 --- /dev/null +++ b/airtrafficsim/geometry.py @@ -0,0 +1,13 @@ +from typing import Generic, NamedTuple + +from .types import ArrayOrFloat + + +# TODO(abrah): make this generic +class Point2D(NamedTuple, Generic[ArrayOrFloat]): + """A coordinate in 2D.""" + + lng: ArrayOrFloat + """Longitude of the point(s) [rad]""" + lat: ArrayOrFloat + """Latitude of the point(s) [rad]""" diff --git a/airtrafficsim/server/__init__.py b/airtrafficsim/server/__init__.py index e69de29..7289a28 100644 --- a/airtrafficsim/server/__init__.py +++ b/airtrafficsim/server/__init__.py @@ -0,0 +1,3 @@ +from typing import Any, TypeAlias + +Czml: TypeAlias = list[dict[str, Any]] diff --git a/airtrafficsim/server/data.py b/airtrafficsim/server/data.py index f8d185f..15f0d85 100644 --- a/airtrafficsim/server/data.py +++ b/airtrafficsim/server/data.py @@ -10,12 +10,14 @@ from PIL import Image import numpy as np -from airtrafficsim.core.navigation import Nav + +from ..core.navigation import Nav +from . import Czml class Data: @staticmethod - def get_nav(lat1, long1, lat2, long2): + def get_nav(lat1: float, long1: float, lat2: float, long2: float) -> Czml: """ Get the navigation waypoint data given @@ -36,7 +38,7 @@ def get_nav(lat1, long1, lat2, long2): JSON CZML file of navigation waypoint data """ - document = [ + document: Czml = [ { "id": "document", "name": "Nav", @@ -44,7 +46,7 @@ def get_nav(lat1, long1, lat2, long2): } ] - fixes = Nav.get_wp_in_area(lat1, long1, lat2, long2) + fixes, navaids = Nav.get_wp_in_area(lat1, long1, lat2, long2) for fix in fixes: document.append( @@ -76,7 +78,14 @@ def get_nav(lat1, long1, lat2, long2): return document @staticmethod - def get_era5_wind(file, lat1, long1, lat2, long2, time): + def get_era5_wind( + file: str, + lat1: float, + long1: float, + lat2: float, + long2: float, + time: str, + ) -> Czml: """ Get the ERA5 wind data image to client @@ -123,17 +132,14 @@ def get_era5_wind(file, lat1, long1, lat2, long2, time): ) ax = fig.add_axes( [0, 0, 1, 1], projection=ccrs.PlateCarree(), frameon=False - ) + ) # type: ignore ax.set_extent([long1, long2, lat1, lat2], crs=ccrs.PlateCarree()) - # ax.set_global() - # ax.coastlines() ax.barbs( data.longitude.values, data.latitude.values, data.u.values, data.v.values, flagcolor="grey", - # sizes=dict(emptybarb=0.25, spacing=0.2, height=0.5), linewidth=0.95, length=5, transform=ccrs.PlateCarree(), ) buf = BytesIO() @@ -167,9 +173,17 @@ def get_era5_wind(file, lat1, long1, lat2, long2, time): }, }, ] + return [] @staticmethod - def get_era5_rain(file, lat1, long1, lat2, long2, time): + def get_era5_rain( + file: str, + lat1: float, + long1: float, + lat2: float, + long2: float, + time: str, + ) -> Czml: """ Get the ERA5 rain data image to client @@ -190,120 +204,124 @@ def get_era5_rain(file, lat1, long1, lat2, long2, time): JSON CZML file of ERA5 rain data image """ # TODO: Improve data loading to avoid repetitive loading - if ( + if not ( Path(__file__) .parent.parent.joinpath("data/weather/era5/", file.split("-", 1)[0]) .is_dir() ): - data = xr.open_dataset( - Path(__file__).parent.parent.joinpath( - "data/weather/era5/", file.split("-", 1)[0] + "/surface.nc" - ) - ).sel(time=datetime.fromisoformat(time), method="pad") - data = data.where( - ( - ((data.latitude >= lat1) & (data.latitude <= lat2)) - & ( - (data.longitude >= (long1 + 360.0) % 360.0) - & (data.longitude <= (long2 + 360.0) % 360.0) - ) - ), - drop=True, - ) - fig = Figure( - figsize=(long2 - long1, lat2 - lat1), facecolor="none", dpi=500 - ) - # fig = Figure(figsize=(360, 180), facecolor='none', dpi=100) - ax = fig.add_axes( - [0, 0, 1, 1], projection=ccrs.PlateCarree(), frameon=False - ) - ax.set_extent([long1, long2, lat1, lat2], crs=ccrs.PlateCarree()) - # ax.set_global() - # ax.coastlines() - colorscale = [ - "#ffffff00", - "#00c9fc", - "#008ff4", - "#3b96ff", - "#018445", - "#01aa35", - "#00cf01", - "#00f906", - "#91ff00", - "#e0d000", - "#ffd201", - "#efb001", - "#f08002", - "#f00001", - "#ce0101", - "#bc016a", - "#ef00f0", - ] - scale = [ - 0, - 0.15, - 0.5, - 1, - 2, - 3, - 5, - 7, - 10, - 15, - 30, - 50, - 75, - 100, - 150, - 200, - 300, - ] - cmap = colors.ListedColormap(colorscale) - norm = colors.BoundaryNorm(scale, len(colorscale)) - # ax.pcolormesh(data.longitude.values, data.latitude.values, data.tp.values*75625.0, - # cmap=cmap, norm=norm, transform=ccrs.PlateCarree()) - ax.contourf( - data.longitude.values, - data.latitude.values, - data.tp.values * 75625.0, - cmap=cmap, - norm=norm, - transform=ccrs.PlateCarree(), + return [] + data = xr.open_dataset( + Path(__file__).parent.parent.joinpath( + "data/weather/era5/", file.split("-", 1)[0] + "/surface.nc" ) - buf = BytesIO() - fig.savefig(buf, format="png") - uri = "data:image/png;base64," + base64.b64encode( - buf.getbuffer() - ).decode("ascii") - return [ - { - "id": "document", - "name": "Weather", - "version": "1.0", - }, - { - "id": "Weather", - "rectangle": { - "coordinates": { - "wsenDegrees": [long1, lat1, long2, lat2], - }, - "height": 0, - "fill": True, - "material": { - "image": { - "image": {"uri": uri}, - "color": { - "rgba": [255, 255, 255, 120], - }, - "transparent": True, + ).sel(time=datetime.fromisoformat(time), method="pad") + data = data.where( + ( + ((data.latitude >= lat1) & (data.latitude <= lat2)) + & ( + (data.longitude >= (long1 + 360.0) % 360.0) + & (data.longitude <= (long2 + 360.0) % 360.0) + ) + ), + drop=True, + ) + fig = Figure( + figsize=(long2 - long1, lat2 - lat1), facecolor="none", dpi=500 + ) + # fig = Figure(figsize=(360, 180), facecolor='none', dpi=100) + ax = fig.add_axes( + (0, 0, 1, 1), projection=ccrs.PlateCarree(), frameon=False + ) + ax.set_extent([long1, long2, lat1, lat2], crs=ccrs.PlateCarree()) # type: ignore + colorscale = [ + "#ffffff00", + "#00c9fc", + "#008ff4", + "#3b96ff", + "#018445", + "#01aa35", + "#00cf01", + "#00f906", + "#91ff00", + "#e0d000", + "#ffd201", + "#efb001", + "#f08002", + "#f00001", + "#ce0101", + "#bc016a", + "#ef00f0", + ] + scale = [ + 0, + 0.15, + 0.5, + 1, + 2, + 3, + 5, + 7, + 10, + 15, + 30, + 50, + 75, + 100, + 150, + 200, + 300, + ] + cmap = colors.ListedColormap(colorscale) + norm = colors.BoundaryNorm(scale, len(colorscale)) + ax.contourf( + data.longitude.values, + data.latitude.values, + data.tp.values * 75625.0, + cmap=cmap, + norm=norm, + transform=ccrs.PlateCarree(), + ) + buf = BytesIO() + fig.savefig(buf, format="png") + uri = "data:image/png;base64," + base64.b64encode( + buf.getbuffer() + ).decode("ascii") + return [ + { + "id": "document", + "name": "Weather", + "version": "1.0", + }, + { + "id": "Weather", + "rectangle": { + "coordinates": { + "wsenDegrees": [long1, lat1, long2, lat2], + }, + "height": 0, + "fill": True, + "material": { + "image": { + "image": {"uri": uri}, + "color": { + "rgba": [255, 255, 255, 120], }, + "transparent": True, }, }, }, - ] + }, + ] @staticmethod - def get_radar_img(file, lat1, long1, lat2, long2, time): + def get_radar_img( + file: str, + lat1: float, + long1: float, + lat2: float, + long2: float, + time: str, + ) -> Czml: """ Get the radar data image to client @@ -469,93 +487,93 @@ def get_radar_img(file, lat1, long1, lat2, long2, time): ] # Input - if ( + if not ( Path(__file__) .parent.parent.joinpath( "data/weather/radar/", file.split("-", 1)[0] ) .is_dir() ): - for file in ( - Path(__file__) - .parent.parent.joinpath( - "data/weather/radar/", file.split("-", 1)[0] - ) - .iterdir() - ): - if ( - datetime.fromisoformat(time + "+00:00") - - datetime.fromisoformat(file.stem + "+00:00") - ).seconds < 3600: - img = Image.open(file, "r") - # Cut the color bar and description - left = 0 - top = 0 - right = 400 - bottom = 400 - img = img.crop((left, top, right, bottom)) - data = np.array(img) + return [] + for fp in ( + Path(__file__) + .parent.parent.joinpath( + "data/weather/radar/", file.split("-", 1)[0] + ) + .iterdir() + ): + if ( + datetime.fromisoformat(time + "+00:00") + - datetime.fromisoformat(fp.stem + "+00:00") + ).seconds > 3600: + return [] + img = Image.open(fp, "r") + # Cut the color bar and description + left = 0 + top = 0 + right = 400 + bottom = 400 + img_cropped = img.crop((left, top, right, bottom)) + data = np.array(img_cropped) - # Computer square distance of color https://en.wikipedia.org/wiki/Color_difference - distant = [ - np.square(data[:, :, 0].flatten() - rain_fall_r[i]) - + np.square(data[:, :, 1].flatten() - rain_fall_g[i]) - + np.square(data[:, :, 2].flatten() - rain_fall_b[i]) - for i in range(len(rain_fall)) - ] - distant = np.stack(distant, axis=-1) - # Find the index of minimum element row-wise in distance array - index = np.argmin(distant, axis=1) - # Grab the rain_fall category value given index of rain_fall array - result = np.take(rain_fall, index) - # Check if the minimum distance is smaller than a confidence interval - CONFIDENCE = 6000.0 - min_distant = np.amin(distant, axis=1) - result = np.where(min_distant < CONFIDENCE, result, 0) - result = result.reshape( - np.abs(bottom - top), np.abs(right - left) - ) + # Computer square distance of color https://en.wikipedia.org/wiki/Color_difference + distant = [ + np.square(data[:, :, 0].flatten() - rain_fall_r[i]) + + np.square(data[:, :, 1].flatten() - rain_fall_g[i]) + + np.square(data[:, :, 2].flatten() - rain_fall_b[i]) + for i in range(len(rain_fall)) + ] + distant = np.stack(distant, axis=-1) + # find index of minimum element row-wise in distance array + # and grab rain_fall category + index = np.argmin(distant, axis=1) + result = np.take(rain_fall, index) + # Check if the minimum distance < confidence interval + CONFIDENCE = 6000.0 + min_distant = np.amin(distant, axis=1) + result = np.where(min_distant < CONFIDENCE, result, 0) + result = result.reshape(np.abs(bottom - top), np.abs(right - left)) - # Plot - fig = Figure( - figsize=(long2 - long1, lat2 - lat1), - facecolor="none", - dpi=500, - ) - ax = fig.add_axes([0, 0, 1, 1], frameon=False) - # ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.PlateCarree(), frameon=False) - # ax.set_extent([long1, long2, lat1, lat2], crs=ccrs.PlateCarree()) - cmap = colors.ListedColormap(colorsList) - norm = colors.BoundaryNorm(scale, len(colorsList)) - ax.imshow(result, cmap=cmap, norm=norm) - buf = BytesIO() - fig.savefig(buf, format="png") - uri = "data:image/png;base64," + base64.b64encode( - buf.getbuffer() - ).decode("ascii") - return [ - { - "id": "document", - "name": "Weather", - "version": "1.0", + # Plot + fig = Figure( + figsize=(long2 - long1, lat2 - lat1), + facecolor="none", + dpi=500, + ) + ax = fig.add_axes((0, 0, 1, 1), frameon=False) + cmap = colors.ListedColormap(colorsList) + norm = colors.BoundaryNorm(scale, len(colorsList)) + ax.imshow(result, cmap=cmap, norm=norm) + buf = BytesIO() + fig.savefig(buf, format="png") + uri = "data:image/png;base64," + base64.b64encode( + buf.getbuffer() + ).decode("ascii") + return [ + { + "id": "document", + "name": "Weather", + "version": "1.0", + }, + { + "id": "Weather", + "rectangle": { + "coordinates": { + "wsenDegrees": [long1, lat1, long2, lat2], }, - { - "id": "Weather", - "rectangle": { - "coordinates": { - "wsenDegrees": [long1, lat1, long2, lat2], - }, - "height": 0, - "fill": True, - "material": { - "image": { - "image": {"uri": uri}, - "color": { - "rgba": [255, 255, 255, 128], - }, - "transparent": True, - }, + "height": 0, + "fill": True, + "material": { + "image": { + "image": {"uri": uri}, + "color": { + "rgba": [255, 255, 255, 128], }, + "transparent": True, }, }, - ] + }, + }, + ] + else: + return [] diff --git a/airtrafficsim/server/replay.py b/airtrafficsim/server/replay.py index e5652da..5ec4961 100644 --- a/airtrafficsim/server/replay.py +++ b/airtrafficsim/server/replay.py @@ -1,21 +1,38 @@ import csv from datetime import datetime, timezone from pathlib import Path +from typing import Literal, TypedDict import numpy as np import pandas as pd +from . import Czml + + +class ReplayDir(TypedDict): + historic: list[str] + simulation: list[str] + simulation_files: dict[str, list[str]] + + +ReplayMode = Literal["replay", "simulation"] +"""AirTrafficSim mode""" + +ReplayCategory = Literal["historic", "simulation"] +"""Replay category""" + class Replay: @staticmethod - def get_replay_dir(): + def get_replay_dir() -> ReplayDir: """ Return a list of historic/simulation data directories in data/replay Returns ------- {} - JSON file of historic file list and simulation file list in data/replay directory + JSON file of historic file list and simulation file list in + data/replay directory """ historic_list = [] for dir in ( @@ -45,8 +62,11 @@ def get_replay_dir(): "simulation_files": simulation_file_list, } + # FIXME(abrah): split this up. @staticmethod - def get_replay_czml(replayCategory, replayFile): + def get_replay_czml( + replayCategory: ReplayCategory, replayFile: str + ) -> Czml: """ Generate CZML file for visualization given replay file name. @@ -78,9 +98,6 @@ def get_replay_czml(replayCategory, replayFile): file_content = pd.read_csv(file) if replayCategory == "historic": - # start = datetime.utcfromtimestamp(file_content.iloc[0]['timestamp']) - # end = datetime.utcfromtimestamp(file_content.iloc[-1]['timestamp']) - id = file.name if ( "timestamp" @@ -186,12 +203,6 @@ def get_replay_czml(replayCategory, replayFile): ] label = label[0::60] - # positions = np.column_stack((file_content['timestamp'].map(lambda x : datetime.utcfromtimestamp(x).isoformat()), - # file_content['long'].values, file_content['lat'].values, file_content['alt'].values/3.2808)).flatten().tolist() - # label = [{"interval": datetime.utcfromtimestamp(time).isoformat()+"/"+end.isoformat(), - # "string": file.name+"\n"+str(alt)+"ft "+str(gspeed)+"kt"} - # for time, alt, gspeed in zip(file_content['timestamp'], file_content['alt'], file_content['gspeed'])] - if start_time is None and end_time is None: start_time = start end_time = end @@ -238,6 +249,10 @@ def get_replay_czml(replayCategory, replayFile): }, } trajectories.append(trajectory) + if start_time is None or end_time is None: + raise RuntimeError( + "panic: no start time or end time" + ) # FIXME(abrah) trajectories.insert( 0, { @@ -312,17 +327,6 @@ def get_replay_czml(replayCategory, replayFile): "pixelSize": 5, "color": {"rgba": [39, 245, 106, 215]}, }, - # "billboard": { - # "image":{ - # "interval": content.iloc[0]['timestamp']+"/"+ content.iloc[-1]['timestamp'], - # "uri": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTYiIGhlaWdodD0iOTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjU5MiIgeT0iMzEyIiB3aWR0aD0iOTYiIGhlaWdodD0iOTYiLz48L2NsaXBQYXRoPjwvZGVmcz48ZyBjbGlwLXBhdGg9InVybCgjY2xpcDApIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNTkyIC0zMTIpIj48cGF0aCBkPSJNNjc0IDM3OCA2NzQgMzY5IDY0NSAzNDguNSA2NDUgMzI5QzY0NSAzMjUuMSA2NDMgMzIwIDY0MCAzMjAgNjM3LjEgMzIwIDYzNSAzMjUuMSA2MzUgMzI5TDYzNSAzNDguNSA2MDYgMzY5IDYwNiAzNzggNjM1IDM2My41IDYzNSAzODUuMyA2MjUgMzk0IDYyNSA0MDAgNjQwIDM5NCA2NTUgNDAwIDY1NSAzOTQgNjQ1IDM4NS4zIDY0NSAzNjMuNSA2NzQgMzc4WiIgZmlsbD0iIzAwQ0MzMyIvPjwvZz48L3N2Zz4=" - # }, - # "scale": 1.0, - # # "rotation": 1.3, - # "alignedAxis": { - # "velocityReference": id+"#position" - # } - # }, "path": { "leadTime": 0, "trailTime": 20, @@ -352,7 +356,9 @@ def get_replay_czml(replayCategory, replayFile): return document @staticmethod - def get_graph_header(mode, replayCategory, replayFile): + def get_graph_header( + mode: ReplayMode, replayCategory: ReplayCategory, replayFile: str + ) -> list[str]: """ Get the list of parameters name of a file suitable for plotting graph. @@ -388,7 +394,13 @@ def get_graph_header(mode, replayCategory, replayFile): return header @staticmethod - def get_graph_data(mode, replayCategory, replayFile, simulationFile, graph): + def get_graph_data( + mode: ReplayMode, + replayCategory: ReplayCategory, + replayFile: str, + simulationFile: str, + graph: str, + ) -> Czml: """ Get the data for the selected parameters to plot a graph. @@ -410,7 +422,7 @@ def get_graph_data(mode, replayCategory, replayFile, simulationFile, graph): if ( mode == "replay" and replayCategory == "simulation" - and graph != "None" + and graph != "None" # FIXME(abrah): adopt Option instead ): df = pd.read_csv( Path(__file__).parent.parent.joinpath( diff --git a/airtrafficsim/server/server.py b/airtrafficsim/server/server.py index 179595b..38d3de1 100644 --- a/airtrafficsim/server/server.py +++ b/airtrafficsim/server/server.py @@ -16,13 +16,11 @@ from flask import Flask, render_template from flask_socketio import SocketIO -from airtrafficsim.server.data import Data - -# import eventlet -from airtrafficsim.server.replay import Replay - -# eventlet.monkey_patch() +from . import Czml +from .data import Data +from .replay import Replay, ReplayCategory, ReplayDir, ReplayMode +# TODO: use fastapi app = Flask( __name__, static_url_path="", @@ -41,30 +39,24 @@ ) # engineio_logger=True -@socketio.on("connect") -def test_connect(): - """ - Debug function to test whether the client is connected. - """ +@socketio.on("connect") # type: ignore +def test_connect() -> None: print("Client connected") -@socketio.on("disconnect") -def test_disconnect(): - """ - Debug function to inform the client is disconnected. - """ +@socketio.on("disconnect") # type: ignore +def test_disconnect() -> None: print("Client disconnected") -@socketio.on("getReplayDir") -def get_replay_dir(): +@socketio.on("getReplayDir") # type: ignore +def get_replay_dir() -> ReplayDir: """Get the list of directories in data/replay""" return Replay.get_replay_dir() -@socketio.on("getReplayCZML") -def get_replay_czml(replayCategory, replayFile): +@socketio.on("getReplayCZML") # type: ignore +def get_replay_czml(replayCategory: ReplayCategory, replayFile: str) -> Czml: """ Generate a CZML file to client for replaying data. @@ -83,8 +75,10 @@ def get_replay_czml(replayCategory, replayFile): return Replay.get_replay_czml(replayCategory, replayFile) -@socketio.on("getGraphHeader") -def get_graph_header(mode, replayCategory, replayFile): +@socketio.on("getGraphHeader") # type: ignore +def get_graph_header( + mode: ReplayMode, replayCategory: ReplayCategory, replayFile: str +) -> list[str]: """ Get the list of parameters name of a file suitable for plotting graph. @@ -105,8 +99,14 @@ def get_graph_header(mode, replayCategory, replayFile): return Replay.get_graph_header(mode, replayCategory, replayFile) -@socketio.on("getGraphData") -def get_graph_data(mode, replayCategory, replayFile, simulationFile, graph): +@socketio.on("getGraphData") # type: ignore +def get_graph_data( + mode: ReplayMode, + replayCategory: ReplayCategory, + replayFile: str, + simulationFile: str, + graph: str, +) -> Czml: """ Get the data for the selected parameters to plot a graph. @@ -129,8 +129,8 @@ def get_graph_data(mode, replayCategory, replayFile, simulationFile, graph): ) -@socketio.on("getSimulationFile") -def get_simulation_file(): +@socketio.on("getSimulationFile") # type: ignore +def get_simulation_file() -> list[str]: """ Get the list of files in airtrafficsim/env/ @@ -148,8 +148,8 @@ def get_simulation_file(): return simulation_list -@socketio.on("runSimulation") -def run_simulation(file): +@socketio.on("runSimulation") # type: ignore +def run_simulation(file: str) -> None: """ Start the simulation given file name. @@ -162,28 +162,31 @@ def run_simulation(file): if file == "ConvertHistoricDemo": socketio.emit( "loadingMsg", - "Converting historic data to simulation data...
Please check the terminal for progress.", + "Converting historic data to simulation data...
" + "Please check the terminal for progress.", ) elif file == "WeatherDemo": socketio.emit( "loadingMsg", - "Downloading weather data...
Please check the terminal for progress.", + "Downloading weather data...
" + "Please check the terminal for progress.", ) else: socketio.emit( "loadingMsg", - "Running simulation...
Please check the terminal for progress.", + "Running simulation...
" + "Please check the terminal for progress.", ) socketio.sleep(0) Env = getattr( import_module("airtrafficsim.data.environment." + file, "..."), file - ) + ) # FIXME(abrah): not ideal. env = Env() env.run(socketio) -@socketio.on("getNav") -def get_Nav(lat1, long1, lat2, long2): +@socketio.on("getNav") # type: ignore +def get_Nav(lat1: float, long1: float, lat2: float, long2: float) -> Czml: """ Get the navigation waypoint data given @@ -206,8 +209,10 @@ def get_Nav(lat1, long1, lat2, long2): return Data.get_nav(lat1, long1, lat2, long2) -@socketio.on("getEra5Wind") -def get_era5_wind(lat1, long1, lat2, long2, file, time): +@socketio.on("getEra5Wind") # type: ignore +def get_era5_wind( + lat1: float, long1: float, lat2: float, long2: float, file: str, time: str +): """ Get the ERA5 wind data image to client @@ -230,8 +235,10 @@ def get_era5_wind(lat1, long1, lat2, long2, file, time): return Data.get_era5_wind(file, lat1, long1, lat2, long2, time) -@socketio.on("getEra5Rain") -def get_era5_rain(lat1, long1, lat2, long2, file, time): +@socketio.on("getEra5Rain") # type: ignore +def get_era5_rain( + lat1: float, long1: float, lat2: float, long2: float, file: str, time: str +) -> Czml: """ Get the ERA5 rain data image to client @@ -254,8 +261,10 @@ def get_era5_rain(lat1, long1, lat2, long2, file, time): return Data.get_era5_rain(file, lat1, long1, lat2, long2, time) -@socketio.on("getRadarImage") -def get_radar_img(lat1, long1, lat2, long2, file, time): +@socketio.on("getRadarImage") # type: ignore +def get_radar_img( + lat1: float, long1: float, lat2: float, long2: float, file: str, time: str +) -> Czml: """ Get the radar data image to client @@ -282,13 +291,14 @@ def get_radar_img(lat1, long1, lat2, long2, file, time): return Data.get_radar_img(file, lat1, long1, lat2, long2, time) +# TODO: check where is index.html @app.route("/") -def serve_client(): +def serve_client() -> str: """Serve client folder to user""" return render_template("index.html") -def run_server(port=6111, host="127.0.0.1"): +def run_server(port: int = 6111, host: str = "127.0.0.1") -> None: # Change host to 0.0.0.0 during deployment """Start the backend server.""" print(f"Running server at http://{host}:{port}") diff --git a/airtrafficsim/types.py b/airtrafficsim/types.py new file mode 100644 index 0000000..0508da3 --- /dev/null +++ b/airtrafficsim/types.py @@ -0,0 +1,14 @@ +from typing import TypeVar + +import numpy as np +import numpy.typing as npt + +array = npt.NDArray[np.floating] +"""A numpy array of floating point numbers.""" +# NOTE: not using npt.ArrayLike because it accepts strings + +ArrayOrFloat = TypeVar("ArrayOrFloat", array, float) + +uint_array = npt.NDArray[np.unsignedinteger] +"""A numpy array of unsigned integers.""" +# for use in array of enums diff --git a/airtrafficsim/utils/calculation.py b/airtrafficsim/utils/calculation.py index 1c95e53..662605a 100644 --- a/airtrafficsim/utils/calculation.py +++ b/airtrafficsim/utils/calculation.py @@ -1,13 +1,19 @@ import numpy as np +from ..types import array + class Cal: """ A utility class for calculation """ + # TODO: refactor to use Point2D namedtuple instead. + # verify we need it in radians. @staticmethod - def cal_great_circle_dist(lat1, long1, lat2, long2): + def cal_great_circle_dist( + lat1: array, long1: array, lat2: array, long2: array + ) -> array: """ Calculate great circle distance in km between two point. @@ -29,7 +35,8 @@ def cal_great_circle_dist(lat1, long1, lat2, long2): Notes ----- - Haversine distance using mean Earth radius 6371.009km for the WGS84 ellipsoid. + Haversine distance using mean Earth radius 6371.009km for the WGS84 + ellipsoid. https://www.movable-type.co.uk/scripts/latlong.html """ a = np.square(np.sin((np.deg2rad(lat2 - lat1)) / 2.0)) + np.cos( @@ -37,10 +44,12 @@ def cal_great_circle_dist(lat1, long1, lat2, long2): ) * np.cos(np.deg2rad(lat2)) * np.square( np.sin((np.deg2rad(long2 - long1)) / 2.0) ) - return 2.0 * 6371.009 * np.arctan2(np.sqrt(a), np.sqrt(1.0 - a)) + return 2.0 * 6371.009 * np.arctan2(np.sqrt(a), np.sqrt(1.0 - a)) # type: ignore @staticmethod - def cal_great_circle_bearing(lat1, long1, lat2, long2): + def cal_great_circle_bearing( + lat1: array, long1: array, lat2: array, long2: array + ) -> array: """ Calculate the great circle bearing of two points. @@ -65,7 +74,7 @@ def cal_great_circle_bearing(lat1, long1, lat2, long2): Initial bearing or forward azimuth https://www.movable-type.co.uk/scripts/latlong.html """ - return np.mod( + bearing = np.mod( ( np.rad2deg( np.arctan2( @@ -81,11 +90,15 @@ def cal_great_circle_bearing(lat1, long1, lat2, long2): ), 360.0, ) + return bearing # type: ignore @staticmethod - def cal_dest_given_dist_bearing(lat, long, bearing, dist): + def cal_dest_given_dist_bearing( + lat: array, long: array, bearing: array, dist: array + ) -> tuple[array, array]: """ - Calculate the destination point(s) given start point(s), bearing(s) and distance(s) + Calculate the destination point(s) given start point(s), bearing(s) and + distance(s) Parameters ---------- @@ -131,10 +144,16 @@ def cal_dest_given_dist_bearing(lat, long, bearing, dist): @staticmethod def cal_cross_track_dist( - path_lat1, path_long1, path_lat2, path_long2, point_lat, point_long - ): + path_lat1: array, + path_long1: array, + path_lat2: array, + path_long2: array, + point_lat: array, + point_long: array, + ) -> array: """ - Calculate the cross track distance between point(s) along a great circle path. + Calculate the cross track distance between point(s) along a great circle + path. Parameters ---------- @@ -158,10 +177,11 @@ def cal_cross_track_dist( Notes ----- - Cross track distance using mean Earth radius 6371.009km for the WGS84 ellipsoid. + Cross track distance using mean Earth radius 6371.009km for the WGS84 + ellipsoid. https://www.movable-type.co.uk/scripts/latlong.html """ - return ( + distance_cross_track = ( np.arcsin( np.sin( Cal.cal_great_circle_dist( @@ -182,11 +202,13 @@ def cal_cross_track_dist( ) * 6371.009 ) + return distance_cross_track # type: ignore @staticmethod - def cal_angle_diff(current_angle, target_angle): + def cal_angle_diff(current_angle: array, target_angle: array) -> array: """ - Calculate the difference of two angle (+ve clockwise, -ve anti-clockwise. + Calculate the difference of two angles + (+ve clockwise, -ve anti-clockwise). Parameters ---------- diff --git a/airtrafficsim/utils/enums.py b/airtrafficsim/utils/enums.py index 1425b47..bc6e1d0 100644 --- a/airtrafficsim/utils/enums.py +++ b/airtrafficsim/utils/enums.py @@ -1,50 +1,37 @@ from enum import IntEnum +# FIXME(abrah): move them into the appropriate modules. + class EngineType(IntEnum): """ - An enumeration for aircraft's engine type. + Aircraft engine type + """ - Attributes - ---------- JET = 1 TURBOPROP = 2 PISTON = 3 - """ - - JET = (1,) - TURBOPROP = (2,) - PISTON = 3 class WakeCat(IntEnum): """ - An enumeration for aircraft's wake category. + Aircraft wake category + """ - Attributes - ---------- J = 1 - Jumbo + """Jumbo""" H = 2 - Heavy + """Heavy""" M = 3 - Medium - L = 4 - Light - """ - - J = (1,) - H = (2,) - M = (3,) + """Medium""" L = 4 class FlightPhase(IntEnum): """ - An enumeration for aircraft's flight phase. + Aircraft flight phase + """ - Attributes - ---------- AT_GATE_ORIGIN = 1 TAXI_ORIGIN = 2 TAKEOFF = 3 @@ -56,128 +43,73 @@ class FlightPhase(IntEnum): LANDING = 9 TAXI_DEST = 10 AT_GATE_DEST = 11 - """ - - AT_GATE_ORIGIN = (1,) - TAXI_ORIGIN = (2,) - TAKEOFF = (3,) - INITIAL_CLIMB = (4,) - CLIMB = (5,) - CRUISE = (6,) - DESCENT = (7,) - APPROACH = (8,) - LANDING = (9,) - TAXI_DEST = (10,) - AT_GATE_DEST = 11 class Config(IntEnum): """ - An enumeration for aircraft's wing and landing gear configuration. + Wing and landing gear configuration + """ - Attributes - ---------- TAKEOFF = 1 INITIAL_CLIMB = 2 CLEAN = 3 APPROACH = 4 LANDING = 5 - """ - - TAKEOFF = (1,) - INITIAL_CLIMB = (2,) - CLEAN = (3,) - APPROACH = (4,) - LANDING = 5 class SpeedMode(IntEnum): """ - An enumeration for aircraft's actual speed mode. - - Attributes - ---------- - CAS = 1 - MACH = 2 + Aircraft actual speed mode """ - CAS = (1,) + CAS = 1 MACH = 2 class VerticalMode(IntEnum): """ - An enumeration for aircraft's actual vertical mode. + Aircraft actual vertical mode + """ - Attributes - ---------- LEVEL = 1 CLIMB = 2 DESCENT = 3 - """ - - LEVEL = (1,) - CLIMB = (2,) - DESCENT = 3 class APSpeedMode(IntEnum): """ - An enumeration for aircraft's autopilot target vertical mode. + Aircraft autopilot target speed mode + """ - Attributes - ---------- CONSTANT_MACH = 1 CONSTANT_CAS = 2 ACCELERATE = 3 DECELERATE = 4 - """ - - CONSTANT_MACH = (1,) - CONSTANT_CAS = (2,) - ACCELERATE = (3,) - DECELERATE = 4 class APThrottleMode(IntEnum): """ - An enumeration for aircraft's autopilot target throttle mode. - - Attributes - ---------- - AUTO = 1, - SPEED = 2 + Aircraft autopilot target throttle mode """ - AUTO = (1,) + AUTO = 1 SPEED = 2 class APVerticalMode(IntEnum): """ - An enumeration for aircraft's autopilot target vertical mode. + Aircraft autopilot target vertical mode + """ - Attributes - ---------- ALT_HOLD = 1 VS = 2 FLC = 3 - """ - - ALT_HOLD = (1,) - VS = (2,) - FLC = 3 class APLateralMode(IntEnum): """ - An enumeration for aircraft's autopilot target lateral mode. - - Attributes - ---------- - HEADING = 1 - LNAV = 2 + Aircraft Lateral Mode """ - HEADING = (1,) + HEADING = 1 LNAV = 2 diff --git a/airtrafficsim/utils/route_detection.py b/airtrafficsim/utils/route_detection.py index 9082b01..9ffd8e7 100644 --- a/airtrafficsim/utils/route_detection.py +++ b/airtrafficsim/utils/route_detection.py @@ -1,9 +1,18 @@ +from typing import Any, NamedTuple + import numpy as np -from airtrafficsim.core.navigation import Nav -from airtrafficsim.utils.calculation import Cal + +from ..core.navigation import Nav +from ..geometry import Point2D +from ..types import ArrayOrFloat, array +from ..utils.calculation import Cal -def distance(a, b): +# FIXME(abrah): specify this as euclidean, not haversine +# TODO(abrah): updated deg -> rad, check logic errors +def distance( + a: Point2D[ArrayOrFloat], b: Point2D[ArrayOrFloat] +) -> ArrayOrFloat: """ Helper function to calculate distance between two points @@ -19,10 +28,13 @@ def distance(a, b): float Distance between two point """ - return np.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) + return ((a.lng - b.lng) ** 2 + (a.lat - b.lat) ** 2) ** 0.5 # type: ignore -def point_line_distance(point, start, end): +# TODO(abrah): updated deg -> rad, check logic errors +def point_line_distance( + point: Point2D[float], start: Point2D[float], end: Point2D[float] +) -> float: """ Helper function to calculate distance between a point and a line @@ -40,18 +52,19 @@ def point_line_distance(point, start, end): float Minimum distance between the point and the line """ - if (start == end).all(): + if start.lat == end.lat and start.lng == end.lng: return distance(point, start) else: n = abs( - (end[0] - start[0]) * (start[1] - point[1]) - - (start[0] - point[0]) * (end[1] - start[1]) + (end.lat - start.lat) * (start.lng - point.lng) + - (start.lat - point.lat) * (end.lng - start.lng) ) - d = np.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2) - return n / d + d = np.sqrt((end.lat - start.lat) ** 2 + (end.lng - start.lng) ** 2) + return n / d # type: ignore -def rdp(points, epsilon): +# TODO(abrah): updated deg -> rad, check logic errors +def rdp(points: list[Point2D[float]], epsilon: float) -> list[Point2D[float]]: """ Reduces a series of points to a simplified version that loses detail, but maintains the general shape of the series. @@ -81,9 +94,17 @@ def rdp(points, epsilon): return results +# NOTE(abrah): what does this do? +class _IdentifiedSidStar(NamedTuple): + ats: Any + trajectory_in_area: array + + def detect_sid_star( - simplified_trajectory, procedure_dict, waypoints_coord_dict -): + simplified_trajectory: array, + procedure_dict: dict[Any, Any], + waypoints_coord_dict: dict[Any, Any], +) -> _IdentifiedSidStar: """ Detect SID and STAR @@ -101,6 +122,7 @@ def detect_sid_star( SID/STAR : string Identified SID/STAR """ + # NOTE(abrah): wrong docstring. area_list = [] ats_list = [] wp_lat = np.array(list(waypoints_coord_dict.values()))[:, 0] @@ -152,10 +174,18 @@ def detect_sid_star( ats_list.append(ats) # print(ats, total_area) - return ats_list[np.argmin(area_list)], trajectory_in_area + return _IdentifiedSidStar( + ats=ats_list[np.argmin(area_list)], + trajectory_in_area=trajectory_in_area, + ) -def get_arrival_data(airport, runway): +class ArrivalData(NamedTuple): + arrivals: dict[str, list[str]] + arrival_waypoints_coord: dict[Any, Any] + + +def get_arrival_data(airport: str, runway: str) -> ArrivalData: """ Get arrival data @@ -176,24 +206,32 @@ def get_arrival_data(airport, runway): lat, long, _ = Nav.get_runway_coord(airport, runway) arrival_procedures = Nav.get_airport_procedures(airport, "STAR") # Get all arrival route and related waypoints - arrival_waypoints = [] + arrival_waypoints: list[str] = [] arrivals_dict = {} for star in arrival_procedures: wp = Nav.get_procedure(airport, "", star)[0] wp = [ele for ele in wp if ele.strip()] arrival_waypoints.extend(wp) arrivals_dict[star] = wp - arrival_waypoints = np.unique(arrival_waypoints) + arrival_waypoints = np.unique(arrival_waypoints).tolist() # type: ignore # Get coordinate of all arrival waypoints arrival_waypoints_coord_dict = {} - for wp in arrival_waypoints: - coord = Nav.get_wp_coord(wp, lat, long) + for name in arrival_waypoints: + coord = Nav.get_wp_coord(name, lat, long) arrival_waypoints_coord_dict[wp] = list(coord) - return arrivals_dict, arrival_waypoints_coord_dict + return ArrivalData( + arrivals=arrivals_dict, + arrival_waypoints_coord=arrival_waypoints_coord_dict, + ) + + +class ApproachData(NamedTuple): + approach: dict[str, list[str]] + approach_waypoints_coord: dict[str, list[float]] -def get_approach_data(airport, runway): +def get_approach_data(airport: str, runway: str) -> ApproachData: """ Get approach data @@ -218,24 +256,26 @@ def get_approach_data(airport, runway): str.replace("I", "") for str in approach_procedures if "I" in str ] # Runway without ils - missed_procedure = [] + missed_procedures = [] for procedure in approach_procedures: hv_runway = [runway for runway in ils_runway if runway in procedure] if len(hv_runway) == 0: - missed_procedure.append(procedure) - approach_procedures = ils + missed_procedure + missed_procedures.append(procedure) # Get all approach route and related waypoints - approach_waypoints = [] + approach_waypoints: list[str] = [] approach_dict = {} - for approach in approach_procedures: + for approach in ils + missed_procedures: wp = Nav.get_procedure("VHHH", "", approach)[0] wp = [ele for ele in wp if ele.strip() and "RW" not in ele] approach_waypoints.extend(wp) approach_dict[approach] = wp - approach_waypoints = np.unique(approach_waypoints) + approach_waypoints = np.unique(approach_waypoints).tolist() # type: ignore # Get coordinate of all approach waypoints approach_waypoints_coord_dict = {} - for wp in approach_waypoints: - coord = Nav.get_wp_coord(wp, lat, long) - approach_waypoints_coord_dict[wp] = list(coord) - return approach_dict, approach_waypoints_coord_dict + for name in approach_waypoints: + coord = Nav.get_wp_coord(name, lat, long) + approach_waypoints_coord_dict[name] = list(coord) + return ApproachData( + approach=approach_dict, + approach_waypoints_coord=approach_waypoints_coord_dict, + ) diff --git a/airtrafficsim/utils/unit_conversion.py b/airtrafficsim/utils/unit_conversion.py index 4d10aef..2685ed2 100644 --- a/airtrafficsim/utils/unit_conversion.py +++ b/airtrafficsim/utils/unit_conversion.py @@ -1,40 +1,44 @@ +from ..types import ArrayOrFloat + + +# FIXME(abrah): get rid of staticmethod. class Unit: @staticmethod - def kts2mps(knots): + def kts2mps(knots: ArrayOrFloat) -> ArrayOrFloat: """Convert knots (1nm/h) to m/s""" return knots * 0.514444444 @staticmethod - def mps2kts(mps): + def mps2kts(mps: ArrayOrFloat) -> ArrayOrFloat: """Convert m/s to knots (1nm/h)""" return mps / 0.514444444 @staticmethod - def nm2m(nm): + def nm2m(nm: ArrayOrFloat) -> ArrayOrFloat: """Convert nautical mile (1 minute of lat/long) to meter""" return nm * 1852.0 @staticmethod - def m2nm(meter): + def m2nm(meter: ArrayOrFloat) -> ArrayOrFloat: """Convert meter to nautical mile (1 minute of lat/long)""" return meter / 1852.0 @staticmethod - def ft2m(feet): + def ft2m(feet: ArrayOrFloat) -> ArrayOrFloat: """Convert feet to meter""" return feet / 3.280839895 @staticmethod - def m2ft(meter): + def m2ft(meter: ArrayOrFloat) -> ArrayOrFloat: """Convert meter to feet""" return meter * 3.280839895 @staticmethod - def ftpm2mps(ftpm): + def ftpm2mps(ftpm: ArrayOrFloat) -> ArrayOrFloat: """Convert feet/min to meter/second""" return ftpm / 196.8503937 @staticmethod - def mps2ftpm(mps): + def mps2ftpm(mps: ArrayOrFloat) -> ArrayOrFloat: """Convert meter/second to feet/min""" return mps * 196.8503937 diff --git a/pyproject.toml b/pyproject.toml index e870553..0f78ecb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ web = [ ] era5 = [ "cartopy>=0.24.1", + "cdsapi>=0.7.5", "xarray>=2024.11.0", ] openap = [ @@ -63,22 +64,21 @@ openap = [ dev = [ "black>=24.10.0", "ipykernel>=6.29.5", - "mkdocs-material[imaging]>=9.5.49", - "mkdocstrings-python>=1.12.2", "mypy>=1.13.0", "ruff>=0.8.3", ] test = [ - "coverage>=7.6.9", + "pytest-cov>=6.0.0", "pytest>=8.3.4", ] docs = [ - "furo>=2024.8.6", - "myst-parser>=4.0.0", - "numpydoc>=1.8.0", - "sphinx>=8.1.3", + "mkdocs-material[imaging]>=9.5.49", + "mkdocstrings-python>=1.12.2", ] +[tool.uv] +default-groups = ["dev", "test", "docs"] + [tool.ruff] lint.select = [ "E", @@ -108,4 +108,4 @@ ignore_missing_imports = true warn_no_return = true warn_redundant_casts = true warn_unused_configs = true -warn_unused_ignores = true +warn_unused_ignores = true \ No newline at end of file diff --git a/tests/test_environment.py b/tests/test_environment.py index 71e2910..7723085 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -1,60 +1,48 @@ -from importlib import import_module - import pandas as pd -def test_demoenv(): - Env = getattr( - import_module("airtrafficsim.data.environment.DemoEnv", "..."), - "DemoEnv", - ) - env = Env() +def test_demoenv() -> None: + from airtrafficsim.data.environment.DemoEnv import DemoEnv + + env = DemoEnv() env.run() df = pd.read_csv(env.file_path) assert df.shape[0] > 1 and df.isnull().values.any() is False -def test_openapdemo(): - Env = getattr( - import_module("airtrafficsim.data.environment.OpenApDemo", "..."), - "OpenApDemo", - ) - env = Env() +def test_openapdemo() -> None: + from airtrafficsim.data.environment.OpenApDemo import OpenApDemo + + env = OpenApDemo() env.run() df = pd.read_csv(env.file_path) assert df.shape[0] > 1 and df.isnull().values.any() is False -def test_fullflightdemo(): - Env = getattr( - import_module("airtrafficsim.data.environment.FullFlightDemo", "..."), - "FullFlightDemo", - ) - env = Env() +def test_fullflightdemo() -> None: + from airtrafficsim.data.environment.FullFlightDemo import FullFlightDemo + + env = FullFlightDemo() env.run() df = pd.read_csv(env.file_path) assert df.shape[0] > 1 and df.isnull().values.any() is False -def test_weatherdemo(): - Env = getattr( - import_module("airtrafficsim.data.environment.WeatherDemo", "..."), - "WeatherDemo", - ) - env = Env() +def test_weatherdemo() -> None: + from airtrafficsim.data.environment.WeatherDemo import WeatherDemo + + env = WeatherDemo() env.run() df = pd.read_csv(env.file_path) assert df.shape[0] > 1 and df.isnull().values.any() is False -def test_converthistoricdemo(): - Env = getattr( - import_module( - "airtrafficsim.data.environment.ConvertHistoricDemo", "..." - ), - "ConvertHistoricDemo", +def test_converthistoricdemo() -> None: + from airtrafficsim.data.environment.ConvertHistoricDemo import ( + ConvertHistoricDemo, ) - env = Env() + + env = ConvertHistoricDemo() env.run() df = pd.read_csv(env.file_path) assert df.shape[0] > 1 and df.isnull().values.any() is False diff --git a/tests/test_ui.py b/tests/test_ui.py index 0cb9743..f0e3d3e 100644 --- a/tests/test_ui.py +++ b/tests/test_ui.py @@ -1,36 +1,38 @@ from pathlib import Path import pytest +from flask.testing import FlaskClient +from flask_socketio import SocketIOTestClient import airtrafficsim.server.server as server @pytest.fixture() -def app(): +def app() -> FlaskClient: return server.app.test_client() @pytest.fixture() -def client(): +def client() -> SocketIOTestClient: return server.socketio.test_client(server.app) -def test_client(app): +def test_client(app: FlaskClient) -> None: response = app.get("/") assert response.data != "" # assert response.status_code == 200 -def test_socketio(client): +def test_socketio(client: SocketIOTestClient) -> None: assert client.is_connected() -def test_get_nav(client): +def test_get_nav(client: SocketIOTestClient) -> None: r = client.emit("getNav", -10, -10, 10, 10, callback=True) assert len(r) > 1 -def test_get_era5_wind(client): +def test_get_era5_wind(client: SocketIOTestClient) -> None: r = client.emit( "getEra5Wind", -10, @@ -44,7 +46,7 @@ def test_get_era5_wind(client): assert r[1]["rectangle"]["material"]["image"]["image"]["uri"] != "" -def test_get_era5_rain(client): +def test_get_era5_rain(client: SocketIOTestClient) -> None: r = client.emit( "getEra5Rain", 10, @@ -58,7 +60,7 @@ def test_get_era5_rain(client): assert r[1]["rectangle"]["material"]["image"]["image"]["uri"] != "" -def test_get_radar_img(client): +def test_get_radar_img(client: SocketIOTestClient) -> None: r = client.emit( "getRadarImage", 15.0, @@ -72,7 +74,7 @@ def test_get_radar_img(client): assert r[1]["rectangle"]["material"]["image"]["image"]["uri"] != "" -def test_get_replay_dir(client): +def test_get_replay_dir(client: SocketIOTestClient) -> None: r = client.emit("getReplayDir", callback=True) path = Path(__file__).parent.parent.joinpath("airtrafficsim/data/result") print(next(iter(path.glob("DemoEnv*"))).name) @@ -113,9 +115,8 @@ def test_get_replay_dir(client): } -def test_get_simulation_file(client): +def test_get_simulation_file(client: SocketIOTestClient) -> None: r = client.emit("getSimulationFile", callback=True) - print(r) assert r == [ "ConvertHistoricDemo", "DemoEnv", @@ -125,12 +126,12 @@ def test_get_simulation_file(client): ] -def test_get_replay_czml(client): +def test_get_replay_czml(client: SocketIOTestClient) -> None: r = client.emit("getReplayCZML", "historic", "2018-05-01", callback=True) assert len(r) > 1 -def test_get_graph_header(client): +def test_get_graph_header(client: SocketIOTestClient) -> None: path = next( iter( Path(__file__) @@ -181,7 +182,7 @@ def test_get_graph_header(client): ] -def test_get_graph_data(client): +def test_get_graph_data(client: SocketIOTestClient) -> None: path = next( iter( Path(__file__) @@ -201,7 +202,7 @@ def test_get_graph_data(client): assert len(r[0]["x"]) > 1 and len(r[0]["y"]) > 1 -def test_run_simulation(client): +def test_run_simulation(client: SocketIOTestClient) -> None: client.emit("runSimulation", "DemoEnv") r = client.get_received() assert len(r) > 0 diff --git a/uv.lock b/uv.lock index 8ec8b4e..d14e5af 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,10 @@ version = 1 -requires-python = ">=3.12" +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version < '3.11'", + "python_full_version == '3.11.*'", + "python_full_version >= '3.12'", +] [[package]] name = "airtrafficsim" @@ -13,6 +18,7 @@ dependencies = [ [package.optional-dependencies] era5 = [ { name = "cartopy" }, + { name = "cdsapi" }, { name = "xarray" }, ] openap = [ @@ -28,25 +34,22 @@ web = [ dev = [ { name = "black" }, { name = "ipykernel" }, - { name = "mkdocs-material", extra = ["imaging"] }, - { name = "mkdocstrings-python" }, { name = "mypy" }, { name = "ruff" }, ] docs = [ - { name = "furo" }, - { name = "myst-parser" }, - { name = "numpydoc" }, - { name = "sphinx" }, + { name = "mkdocs-material", extra = ["imaging"] }, + { name = "mkdocstrings-python" }, ] test = [ - { name = "coverage" }, { name = "pytest" }, + { name = "pytest-cov" }, ] [package.metadata] requires-dist = [ { name = "cartopy", marker = "extra == 'era5'", specifier = ">=0.24.1" }, + { name = "cdsapi", marker = "extra == 'era5'", specifier = ">=0.7.5" }, { name = "eventlet", marker = "extra == 'web'", specifier = ">=0.38.2" }, { name = "flask", marker = "extra == 'web'", specifier = ">=3.1.0" }, { name = "flask-socketio", marker = "extra == 'web'", specifier = ">=5.4.1" }, @@ -60,29 +63,16 @@ requires-dist = [ dev = [ { name = "black", specifier = ">=24.10.0" }, { name = "ipykernel", specifier = ">=6.29.5" }, - { name = "mkdocs-material", extras = ["imaging"], specifier = ">=9.5.49" }, - { name = "mkdocstrings-python", specifier = ">=1.12.2" }, { name = "mypy", specifier = ">=1.13.0" }, { name = "ruff", specifier = ">=0.8.3" }, ] docs = [ - { name = "furo", specifier = ">=2024.8.6" }, - { name = "myst-parser", specifier = ">=4.0.0" }, - { name = "numpydoc", specifier = ">=1.8.0" }, - { name = "sphinx", specifier = ">=8.1.3" }, + { name = "mkdocs-material", extras = ["imaging"], specifier = ">=9.5.49" }, + { name = "mkdocstrings-python", specifier = ">=1.12.2" }, ] test = [ - { name = "coverage", specifier = ">=7.6.9" }, { name = "pytest", specifier = ">=8.3.4" }, -] - -[[package]] -name = "alabaster" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, + { name = "pytest-cov", specifier = ">=6.0.0" }, ] [[package]] @@ -104,24 +94,21 @@ wheels = [ ] [[package]] -name = "babel" -version = "2.16.0" +name = "attrs" +version = "24.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, + { url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 }, ] [[package]] -name = "beautifulsoup4" -version = "4.12.3" +name = "babel" +version = "2.16.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 } +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, ] [[package]] @@ -143,9 +130,19 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 } wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/f3/465c0eb5cddf7dbbfe1fecd9b875d1dcf51b88923cd2c1d7e9ab95c6336b/black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812", size = 1623211 }, + { url = "https://files.pythonhosted.org/packages/df/57/b6d2da7d200773fdfcc224ffb87052cf283cec4d7102fab450b4a05996d8/black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea", size = 1457139 }, + { url = "https://files.pythonhosted.org/packages/6e/c5/9023b7673904a5188f9be81f5e129fff69f51f5515655fbd1d5a4e80a47b/black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f", size = 1753774 }, + { url = "https://files.pythonhosted.org/packages/e1/32/df7f18bd0e724e0d9748829765455d6643ec847b3f87e77456fc99d0edab/black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e", size = 1414209 }, + { url = "https://files.pythonhosted.org/packages/c2/cc/7496bb63a9b06a954d3d0ac9fe7a73f3bf1cd92d7a58877c27f4ad1e9d41/black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", size = 1607468 }, + { url = "https://files.pythonhosted.org/packages/2b/e3/69a738fb5ba18b5422f50b4f143544c664d7da40f09c13969b2fd52900e0/black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", size = 1437270 }, + { url = "https://files.pythonhosted.org/packages/c9/9b/2db8045b45844665c720dcfe292fdaf2e49825810c0103e1191515fc101a/black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", size = 1737061 }, + { url = "https://files.pythonhosted.org/packages/a3/95/17d4a09a5be5f8c65aa4a361444d95edc45def0de887810f508d3f65db7a/black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", size = 1423293 }, { url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 }, { url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 }, { url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 }, @@ -208,6 +205,14 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/e0/75/94aff4fef338887641aa780d13795609861e6e9f9593bd66d4917ab7954b/cartopy-0.24.1.tar.gz", hash = "sha256:01c910d5634c69a7efdec46e0a17d473d2328767f001d4dc0b5c4b48e585c8bd", size = 10741277 } wheels = [ + { url = "https://files.pythonhosted.org/packages/57/41/9dd14e3ee3f7a0546768c11a8f4a37b1c09fc4868b992f431431d526502b/Cartopy-0.24.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce0c83314570c61a695a1f7c3a4a22dc75f79d28f4c68b88a8aeaf13d6a2343c", size = 10982199 }, + { url = "https://files.pythonhosted.org/packages/72/57/8b4a3856aaf4c600504566d7d956928b79d8b17e8d3a1c70060e5f90124f/Cartopy-0.24.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:511f992340baea2c171cb17b3ef595537e5355640f3baa7ac895de25df016a70", size = 10971756 }, + { url = "https://files.pythonhosted.org/packages/ea/50/e5170302a62259f34289ff7f4944a32ac04a49b38713d001873732742726/Cartopy-0.24.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54f4d23961e0f9436baaf4747928361ccdcc893fa9b7bad9f615551bc8aa3fe8", size = 11658621 }, + { url = "https://files.pythonhosted.org/packages/0b/78/7d77c72c85371f5d87088887ec05fd3701dfdcd640845f85378341a355de/Cartopy-0.24.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0b55526b605a9dee4fa3d7e5659b6d7d9d30e609bc5e62487bc4f7d8e90873b", size = 10960201 }, + { url = "https://files.pythonhosted.org/packages/c4/f0/eaa16216c8b91cfd433b60e79080ffaf9e470bb5c939662835faa40fd347/Cartopy-0.24.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a14638b63d7df2858f73e9f8f4f4826d7c9cf13781aa6824fa0134fbaebbd98", size = 10982474 }, + { url = "https://files.pythonhosted.org/packages/63/99/681a7ae5e572343e15ce8697dd4b41f49d45fe89f5e5d8b122bff0f8165c/Cartopy-0.24.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d74b4a3eae9e570f474276fb61847112cdccdead396ec2ddad226dad9eaf4564", size = 10971918 }, + { url = "https://files.pythonhosted.org/packages/04/87/8dc9249e67c635a5c08ae81d4243a1ad69a1b91b703d3ab9be7fa78efc2b/Cartopy-0.24.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa38fb216cfd16cc266fd6f86b60ebdf0056839b490f38d2d89229b03abc877", size = 11718335 }, + { url = "https://files.pythonhosted.org/packages/4f/ce/ba4baced164ecd78b4109cd611d7b64d256f012784e944c1b0f6f5dff5c1/Cartopy-0.24.1-cp311-cp311-win_amd64.whl", hash = "sha256:f440ddb61171319adf34ecec4d91202864cd514a7bc8a252e0ff7788a6604031", size = 10960539 }, { url = "https://files.pythonhosted.org/packages/6e/76/774a4f808c6a4fc19b87c2cc38dd8731d413aad606689451c017ff93ad12/Cartopy-0.24.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a984e33977daed8f760c09c331c8368a6af060db1190af89d74a027c272e39c3", size = 10983939 }, { url = "https://files.pythonhosted.org/packages/2f/48/8517d5d1cc56ce5c4abda1de6454593474a23412115a543f7981aa7e4377/Cartopy-0.24.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71d8a6d061d0764aba3baf357a68f3d73796a8a46d34b8c9fb241171b273c69e", size = 10972374 }, { url = "https://files.pythonhosted.org/packages/c8/84/cb1577d5ac2f0deb002001c6e25b291735151c8c3033c97f212dc482ef72/Cartopy-0.24.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f354a1d902a8d6ee33b099acc86ac2e1af528bbc0ea718b834111c97e604981", size = 11715215 }, @@ -218,6 +223,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/93/31/50bf07ba820c5aa5d4e674d72cdb5da90bbd012ba1b9c6c95c3f96afe233/Cartopy-0.24.1-cp313-cp313-win_amd64.whl", hash = "sha256:b17cf23dd74d0a922c2a5682dacef3c0bf89fa8c0bd0eae96b87fb684f966b15", size = 10959830 }, ] +[[package]] +name = "cdsapi" +version = "0.7.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "datapi" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/62/81b38105ef75486308179d510360c1aa251ce0b7d17fe88c0e1de05c6c94/cdsapi-0.7.5.tar.gz", hash = "sha256:55221c573b8cefe83cc0bfe01a3d31213c82bf9acce70455350dd24b8095c23a", size = 13188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/c4/49f01f1382d449581d5d3db0fa49ccc23a1b4f91d615108b631c7cff40cd/cdsapi-0.7.5-py2.py3-none-any.whl", hash = "sha256:8586b837aea89ceeae379b388fbb0ace0a19b94b221f731c65632417007f69fb", size = 12201 }, +] + [[package]] name = "certifi" version = "2024.12.14" @@ -236,6 +255,30 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } wheels = [ + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, @@ -266,6 +309,36 @@ version = "3.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } wheels = [ + { url = "https://files.pythonhosted.org/packages/69/8b/825cc84cf13a28bfbcba7c416ec22bf85a9584971be15b21dd8300c65b7f/charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", size = 196363 }, + { url = "https://files.pythonhosted.org/packages/23/81/d7eef6a99e42c77f444fdd7bc894b0ceca6c3a95c51239e74a722039521c/charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", size = 125639 }, + { url = "https://files.pythonhosted.org/packages/21/67/b4564d81f48042f520c948abac7079356e94b30cb8ffb22e747532cf469d/charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", size = 120451 }, + { url = "https://files.pythonhosted.org/packages/c2/72/12a7f0943dd71fb5b4e7b55c41327ac0a1663046a868ee4d0d8e9c369b85/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", size = 140041 }, + { url = "https://files.pythonhosted.org/packages/67/56/fa28c2c3e31217c4c52158537a2cf5d98a6c1e89d31faf476c89391cd16b/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", size = 150333 }, + { url = "https://files.pythonhosted.org/packages/f9/d2/466a9be1f32d89eb1554cf84073a5ed9262047acee1ab39cbaefc19635d2/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", size = 142921 }, + { url = "https://files.pythonhosted.org/packages/f8/01/344ec40cf5d85c1da3c1f57566c59e0c9b56bcc5566c08804a95a6cc8257/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", size = 144785 }, + { url = "https://files.pythonhosted.org/packages/73/8b/2102692cb6d7e9f03b9a33a710e0164cadfce312872e3efc7cfe22ed26b4/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", size = 146631 }, + { url = "https://files.pythonhosted.org/packages/d8/96/cc2c1b5d994119ce9f088a9a0c3ebd489d360a2eb058e2c8049f27092847/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", size = 140867 }, + { url = "https://files.pythonhosted.org/packages/c9/27/cde291783715b8ec30a61c810d0120411844bc4c23b50189b81188b273db/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", size = 149273 }, + { url = "https://files.pythonhosted.org/packages/3a/a4/8633b0fc1a2d1834d5393dafecce4a1cc56727bfd82b4dc18fc92f0d3cc3/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", size = 152437 }, + { url = "https://files.pythonhosted.org/packages/64/ea/69af161062166b5975ccbb0961fd2384853190c70786f288684490913bf5/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", size = 150087 }, + { url = "https://files.pythonhosted.org/packages/3b/fd/e60a9d9fd967f4ad5a92810138192f825d77b4fa2a557990fd575a47695b/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", size = 145142 }, + { url = "https://files.pythonhosted.org/packages/6d/02/8cb0988a1e49ac9ce2eed1e07b77ff118f2923e9ebd0ede41ba85f2dcb04/charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", size = 94701 }, + { url = "https://files.pythonhosted.org/packages/d6/20/f1d4670a8a723c46be695dff449d86d6092916f9e99c53051954ee33a1bc/charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", size = 102191 }, + { url = "https://files.pythonhosted.org/packages/9c/61/73589dcc7a719582bf56aae309b6103d2762b526bffe189d635a7fcfd998/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", size = 193339 }, + { url = "https://files.pythonhosted.org/packages/77/d5/8c982d58144de49f59571f940e329ad6e8615e1e82ef84584c5eeb5e1d72/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", size = 124366 }, + { url = "https://files.pythonhosted.org/packages/bf/19/411a64f01ee971bed3231111b69eb56f9331a769072de479eae7de52296d/charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", size = 118874 }, + { url = "https://files.pythonhosted.org/packages/4c/92/97509850f0d00e9f14a46bc751daabd0ad7765cff29cdfb66c68b6dad57f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", size = 138243 }, + { url = "https://files.pythonhosted.org/packages/e2/29/d227805bff72ed6d6cb1ce08eec707f7cfbd9868044893617eb331f16295/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", size = 148676 }, + { url = "https://files.pythonhosted.org/packages/13/bc/87c2c9f2c144bedfa62f894c3007cd4530ba4b5351acb10dc786428a50f0/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", size = 141289 }, + { url = "https://files.pythonhosted.org/packages/eb/5b/6f10bad0f6461fa272bfbbdf5d0023b5fb9bc6217c92bf068fa5a99820f5/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", size = 142585 }, + { url = "https://files.pythonhosted.org/packages/3b/a0/a68980ab8a1f45a36d9745d35049c1af57d27255eff8c907e3add84cf68f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", size = 144408 }, + { url = "https://files.pythonhosted.org/packages/d7/a1/493919799446464ed0299c8eef3c3fad0daf1c3cd48bff9263c731b0d9e2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", size = 139076 }, + { url = "https://files.pythonhosted.org/packages/fb/9d/9c13753a5a6e0db4a0a6edb1cef7aee39859177b64e1a1e748a6e3ba62c2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", size = 146874 }, + { url = "https://files.pythonhosted.org/packages/75/d2/0ab54463d3410709c09266dfb416d032a08f97fd7d60e94b8c6ef54ae14b/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", size = 150871 }, + { url = "https://files.pythonhosted.org/packages/8d/c9/27e41d481557be53d51e60750b85aa40eaf52b841946b3cdeff363105737/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", size = 148546 }, + { url = "https://files.pythonhosted.org/packages/ee/44/4f62042ca8cdc0cabf87c0fc00ae27cd8b53ab68be3605ba6d071f742ad3/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", size = 143048 }, + { url = "https://files.pythonhosted.org/packages/01/f8/38842422988b795220eb8038745d27a675ce066e2ada79516c118f291f07/charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", size = 94389 }, + { url = "https://files.pythonhosted.org/packages/0b/6e/b13bd47fa9023b3699e94abf565b5a2f0b0be6e9ddac9812182596ee62e4/charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", size = 101752 }, { url = "https://files.pythonhosted.org/packages/d3/0b/4b7a70987abf9b8196845806198975b6aab4ce016632f817ad758a5aa056/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", size = 194445 }, { url = "https://files.pythonhosted.org/packages/50/89/354cc56cf4dd2449715bc9a0f54f3aef3dc700d2d62d1fa5bbea53b13426/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", size = 125275 }, { url = "https://files.pythonhosted.org/packages/fa/44/b730e2a2580110ced837ac083d8ad222343c96bb6b66e9e4e706e4d0b6df/charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", size = 119020 }, @@ -341,6 +414,26 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/25/c2/fc7193cc5383637ff390a712e88e4ded0452c9fbcf84abe3de5ea3df1866/contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", size = 13465753 } wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/80937fe3efe0edacf67c9a20b955139a1a622730042c1ea991956f2704ad/contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab", size = 268466 }, + { url = "https://files.pythonhosted.org/packages/82/1d/e3eaebb4aa2d7311528c048350ca8e99cdacfafd99da87bc0a5f8d81f2c2/contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124", size = 253314 }, + { url = "https://files.pythonhosted.org/packages/de/f3/d796b22d1a2b587acc8100ba8c07fb7b5e17fde265a7bb05ab967f4c935a/contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1", size = 312003 }, + { url = "https://files.pythonhosted.org/packages/bf/f5/0e67902bc4394daee8daa39c81d4f00b50e063ee1a46cb3938cc65585d36/contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b", size = 351896 }, + { url = "https://files.pythonhosted.org/packages/1f/d6/e766395723f6256d45d6e67c13bb638dd1fa9dc10ef912dc7dd3dcfc19de/contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453", size = 320814 }, + { url = "https://files.pythonhosted.org/packages/a9/57/86c500d63b3e26e5b73a28b8291a67c5608d4aa87ebd17bd15bb33c178bc/contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3", size = 324969 }, + { url = "https://files.pythonhosted.org/packages/b8/62/bb146d1289d6b3450bccc4642e7f4413b92ebffd9bf2e91b0404323704a7/contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277", size = 1265162 }, + { url = "https://files.pythonhosted.org/packages/18/04/9f7d132ce49a212c8e767042cc80ae390f728060d2eea47058f55b9eff1c/contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595", size = 1324328 }, + { url = "https://files.pythonhosted.org/packages/46/23/196813901be3f97c83ababdab1382e13e0edc0bb4e7b49a7bff15fcf754e/contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697", size = 173861 }, + { url = "https://files.pythonhosted.org/packages/e0/82/c372be3fc000a3b2005061ca623a0d1ecd2eaafb10d9e883a2fc8566e951/contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e", size = 218566 }, + { url = "https://files.pythonhosted.org/packages/12/bb/11250d2906ee2e8b466b5f93e6b19d525f3e0254ac8b445b56e618527718/contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b", size = 269555 }, + { url = "https://files.pythonhosted.org/packages/67/71/1e6e95aee21a500415f5d2dbf037bf4567529b6a4e986594d7026ec5ae90/contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc", size = 254549 }, + { url = "https://files.pythonhosted.org/packages/31/2c/b88986e8d79ac45efe9d8801ae341525f38e087449b6c2f2e6050468a42c/contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86", size = 313000 }, + { url = "https://files.pythonhosted.org/packages/c4/18/65280989b151fcf33a8352f992eff71e61b968bef7432fbfde3a364f0730/contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6", size = 352925 }, + { url = "https://files.pythonhosted.org/packages/f5/c7/5fd0146c93220dbfe1a2e0f98969293b86ca9bc041d6c90c0e065f4619ad/contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85", size = 323693 }, + { url = "https://files.pythonhosted.org/packages/85/fc/7fa5d17daf77306840a4e84668a48ddff09e6bc09ba4e37e85ffc8e4faa3/contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c", size = 326184 }, + { url = "https://files.pythonhosted.org/packages/ef/e7/104065c8270c7397c9571620d3ab880558957216f2b5ebb7e040f85eeb22/contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291", size = 1268031 }, + { url = "https://files.pythonhosted.org/packages/e2/4a/c788d0bdbf32c8113c2354493ed291f924d4793c4a2e85b69e737a21a658/contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f", size = 1325995 }, + { url = "https://files.pythonhosted.org/packages/a6/e6/a2f351a90d955f8b0564caf1ebe4b1451a3f01f83e5e3a414055a5b8bccb/contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375", size = 174396 }, + { url = "https://files.pythonhosted.org/packages/a8/7e/cd93cab453720a5d6cb75588cc17dcdc08fc3484b9de98b885924ff61900/contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9", size = 219787 }, { url = "https://files.pythonhosted.org/packages/37/6b/175f60227d3e7f5f1549fcb374592be311293132207e451c3d7c654c25fb/contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509", size = 271494 }, { url = "https://files.pythonhosted.org/packages/6b/6a/7833cfae2c1e63d1d8875a50fd23371394f540ce809d7383550681a1fa64/contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc", size = 255444 }, { url = "https://files.pythonhosted.org/packages/7f/b3/7859efce66eaca5c14ba7619791b084ed02d868d76b928ff56890d2d059d/contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454", size = 307628 }, @@ -371,6 +464,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f0/ed/92d86f183a8615f13f6b9cbfc5d4298a509d6ce433432e21da838b4b63f4/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", size = 1318403 }, { url = "https://files.pythonhosted.org/packages/b3/0e/c8e4950c77dcfc897c71d61e56690a0a9df39543d2164040301b5df8e67b/contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", size = 185117 }, { url = "https://files.pythonhosted.org/packages/c1/31/1ae946f11dfbd229222e6d6ad8e7bd1891d3d48bde5fbf7a0beb9491f8e3/contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", size = 236668 }, + { url = "https://files.pythonhosted.org/packages/3e/4f/e56862e64b52b55b5ddcff4090085521fc228ceb09a88390a2b103dccd1b/contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6", size = 265605 }, + { url = "https://files.pythonhosted.org/packages/b0/2e/52bfeeaa4541889f23d8eadc6386b442ee2470bd3cff9baa67deb2dd5c57/contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750", size = 315040 }, + { url = "https://files.pythonhosted.org/packages/52/94/86bfae441707205634d80392e873295652fc313dfd93c233c52c4dc07874/contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53", size = 218221 }, ] [[package]] @@ -379,6 +475,26 @@ version = "7.6.9" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/5b/d2/c25011f4d036cf7e8acbbee07a8e09e9018390aee25ba085596c4b83d510/coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", size = 801710 } wheels = [ + { url = "https://files.pythonhosted.org/packages/49/f3/f830fb53bf7e4f1d5542756f61d9b740352a188f43854aab9409c8cdeb18/coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", size = 207024 }, + { url = "https://files.pythonhosted.org/packages/4e/e3/ea5632a3a6efd00ab0a791adc0f3e48512097a757ee7dcbee5505f57bafa/coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", size = 207463 }, + { url = "https://files.pythonhosted.org/packages/e4/ae/18ff8b5580e27e62ebcc888082aa47694c2772782ea7011ddf58e377e98f/coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", size = 235902 }, + { url = "https://files.pythonhosted.org/packages/6a/52/57030a8d15ab935624d298360f0a6704885578e39f7b4f68569e59f5902d/coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", size = 233806 }, + { url = "https://files.pythonhosted.org/packages/d0/c5/4466602195ecaced298d55af1e29abceb812addabefd5bd9116a204f7bab/coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", size = 234966 }, + { url = "https://files.pythonhosted.org/packages/b0/1c/55552c3009b7bf96732e36548596ade771c87f89cf1f5a8e3975b33539b5/coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", size = 234029 }, + { url = "https://files.pythonhosted.org/packages/bb/7d/da3dca6878701182ea42c51df47a47c80eaef2a76f5aa3e891dc2a8cce3f/coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", size = 232494 }, + { url = "https://files.pythonhosted.org/packages/28/cc/39de85ac1d5652bc34ff2bee39ae251b1fdcaae53fab4b44cab75a432bc0/coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", size = 233611 }, + { url = "https://files.pythonhosted.org/packages/d1/2b/7eb011a9378911088708f121825a71134d0c15fac96972a0ae7a8f5a4049/coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", size = 209712 }, + { url = "https://files.pythonhosted.org/packages/5b/35/c3f40a2269b416db34ce1dedf682a7132c26f857e33596830fa4deebabf9/coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", size = 210553 }, + { url = "https://files.pythonhosted.org/packages/b1/91/b3dc2f7f38b5cca1236ab6bbb03e84046dd887707b4ec1db2baa47493b3b/coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", size = 207133 }, + { url = "https://files.pythonhosted.org/packages/0d/2b/53fd6cb34d443429a92b3ec737f4953627e38b3bee2a67a3c03425ba8573/coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", size = 207577 }, + { url = "https://files.pythonhosted.org/packages/74/f2/68edb1e6826f980a124f21ea5be0d324180bf11de6fd1defcf9604f76df0/coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", size = 239524 }, + { url = "https://files.pythonhosted.org/packages/d3/83/8fec0ee68c2c4a5ab5f0f8527277f84ed6f2bd1310ae8a19d0c5532253ab/coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", size = 236925 }, + { url = "https://files.pythonhosted.org/packages/8b/20/8f50e7c7ad271144afbc2c1c6ec5541a8c81773f59352f8db544cad1a0ec/coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", size = 238792 }, + { url = "https://files.pythonhosted.org/packages/6f/62/4ac2e5ad9e7a5c9ec351f38947528e11541f1f00e8a0cdce56f1ba7ae301/coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", size = 237682 }, + { url = "https://files.pythonhosted.org/packages/58/2f/9d2203f012f3b0533c73336c74134b608742be1ce475a5c72012573cfbb4/coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", size = 236310 }, + { url = "https://files.pythonhosted.org/packages/33/6d/31f6ab0b4f0f781636075f757eb02141ea1b34466d9d1526dbc586ed7078/coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", size = 237096 }, + { url = "https://files.pythonhosted.org/packages/7d/fb/e14c38adebbda9ed8b5f7f8e03340ac05d68d27b24397f8d47478927a333/coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", size = 209682 }, + { url = "https://files.pythonhosted.org/packages/a4/11/a782af39b019066af83fdc0e8825faaccbe9d7b19a803ddb753114b429cc/coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", size = 210542 }, { url = "https://files.pythonhosted.org/packages/60/52/b16af8989a2daf0f80a88522bd8e8eed90b5fcbdecf02a6888f3e80f6ba7/coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", size = 207325 }, { url = "https://files.pythonhosted.org/packages/0f/79/6b7826fca8846c1216a113227b9f114ac3e6eacf168b4adcad0cb974aaca/coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", size = 207563 }, { url = "https://files.pythonhosted.org/packages/a7/07/0bc73da0ccaf45d0d64ef86d33b7d7fdeef84b4c44bf6b85fb12c215c5a6/coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", size = 240580 }, @@ -409,6 +525,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/d0/8e258f6c3a527c1655602f4f576215e055ac704de2d101710a71a2affac2/coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9", size = 247783 }, { url = "https://files.pythonhosted.org/packages/a9/0d/1e4a48d289429d38aae3babdfcadbf35ca36bdcf3efc8f09b550a845bdb5/coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b", size = 210646 }, { url = "https://files.pythonhosted.org/packages/26/74/b0729f196f328ac55e42b1e22ec2f16d8bcafe4b8158a26ec9f1cdd1d93e/coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", size = 211815 }, + { url = "https://files.pythonhosted.org/packages/15/0e/4ac9035ee2ee08d2b703fdad2d84283ec0bad3b46eb4ad6affb150174cb6/coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", size = 199270 }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, ] [[package]] @@ -433,12 +555,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, ] +[[package]] +name = "datapi" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "multiurl" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/95/e4abd8958a15793a9d6df1d6f0bcff2ac245901d9cc44dfb14be3bbb1bf6/datapi-0.1.1.tar.gz", hash = "sha256:7526169c61d103bd8585a54778aa234ba89ef6f6093d2079db9d089ad5e448e0", size = 43438 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/a2/8f4f500bfa3b407afdd3bd5987dd0d5f0b1dcfb9cfd88a95a753bd76c71a/datapi-0.1.1-py3-none-any.whl", hash = "sha256:3c8e9c7c5a29b9b392633d9527266080868d3f411b78e05acac4cd9304599bd9", size = 26899 }, +] + [[package]] name = "debugpy" version = "1.8.11" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/bc/e7/666f4c9b0e24796af50aadc28d36d21c2e01e831a934535f956e09b3650c/debugpy-1.8.11.tar.gz", hash = "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57", size = 1640124 } wheels = [ + { url = "https://files.pythonhosted.org/packages/26/e6/4cf7422eaa591b4c7d6a9fde224095dac25283fdd99d90164f28714242b0/debugpy-1.8.11-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd", size = 2075100 }, + { url = "https://files.pythonhosted.org/packages/83/3a/e163de1df5995d95760a4d748b02fbefb1c1bf19e915b664017c40435dbf/debugpy-1.8.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f", size = 3559724 }, + { url = "https://files.pythonhosted.org/packages/27/6c/327e19fd1bf428a1efe1a6f97b306689c54c2cebcf871b66674ead718756/debugpy-1.8.11-cp310-cp310-win32.whl", hash = "sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737", size = 5178068 }, + { url = "https://files.pythonhosted.org/packages/49/80/359ff8aa388f0bd4a48f0fa9ce3606396d576657ac149c6fba3cc7de8adb/debugpy-1.8.11-cp310-cp310-win_amd64.whl", hash = "sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1", size = 5210109 }, + { url = "https://files.pythonhosted.org/packages/7c/58/8e3f7ec86c1b7985a232667b5df8f3b1b1c8401028d8f4d75e025c9556cd/debugpy-1.8.11-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296", size = 2173656 }, + { url = "https://files.pythonhosted.org/packages/d2/03/95738a68ade2358e5a4d63a2fd8e7ed9ad911001cfabbbb33a7f81343945/debugpy-1.8.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1", size = 3132464 }, + { url = "https://files.pythonhosted.org/packages/ca/f4/18204891ab67300950615a6ad09b9de236203a9138f52b3b596fa17628ca/debugpy-1.8.11-cp311-cp311-win32.whl", hash = "sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9", size = 5103637 }, + { url = "https://files.pythonhosted.org/packages/3b/90/3775e301cfa573b51eb8a108285681f43f5441dc4c3916feed9f386ef861/debugpy-1.8.11-cp311-cp311-win_amd64.whl", hash = "sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e", size = 5127862 }, { url = "https://files.pythonhosted.org/packages/c6/ae/2cf26f3111e9d94384d9c01e9d6170188b0aeda15b60a4ac6457f7c8a26f/debugpy-1.8.11-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308", size = 2498756 }, { url = "https://files.pythonhosted.org/packages/b0/16/ec551789d547541a46831a19aa15c147741133da188e7e6acf77510545a7/debugpy-1.8.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768", size = 4219136 }, { url = "https://files.pythonhosted.org/packages/72/6f/b2b3ce673c55f882d27a6eb04a5f0c68bcad6b742ac08a86d8392ae58030/debugpy-1.8.11-cp312-cp312-win32.whl", hash = "sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b", size = 5224440 }, @@ -477,15 +622,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, ] -[[package]] -name = "docutils" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, -] - [[package]] name = "eventlet" version = "0.38.2" @@ -499,6 +635,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/07/00feb2c708d71796e190a3051a0d530a4922bfb6b346aa8302725840698c/eventlet-0.38.2-py3-none-any.whl", hash = "sha256:4a2e3cbc53917c8f39074ccf689501168563d3a4df59e9cddd5e9d3b7f85c599", size = 363192 }, ] +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + [[package]] name = "executing" version = "2.1.0" @@ -526,15 +671,15 @@ wheels = [ [[package]] name = "flask-socketio" -version = "5.4.1" +version = "5.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flask" }, { name = "python-socketio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/16/53/a8eece1e8729617056bb754a82f9cbba1011bff1e6a6f82db56958c915cc/flask_socketio-5.4.1.tar.gz", hash = "sha256:2e9b8864a5be37ca54f6c76a4d06b1ac5e0df61fde12d03afc81ab4057e1eb86", size = 37261 } +sdist = { url = "https://files.pythonhosted.org/packages/64/f3/ee59938a844aec626b31d457942cd122be1d94c70df6d821099a0dc7e815/flask_socketio-5.5.0.tar.gz", hash = "sha256:2b58a8015d7aece71d5e822bacdf28672d363e72b33366d77da6fc9bacc86641", size = 37386 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/d2/9fc23b5bd5c7457b69d91304e2c9244596097327c645cd44de6271badc17/Flask_SocketIO-5.4.1-py3-none-any.whl", hash = "sha256:895da879d162781b9193cbb8fe8f3cf25b263ff242980d5c5e6c16d3c03930d2", size = 18185 }, + { url = "https://files.pythonhosted.org/packages/84/39/1ea66fcc8c64230128c7911fe1a27be70f7bd65b5af70cdcb2f27caedc0f/Flask_SocketIO-5.5.0-py3-none-any.whl", hash = "sha256:1e44564ddb5019101b0d475fd538d3436aca9c081a79749a317a9bf0a831e5fd", size = 18260 }, ] [[package]] @@ -543,6 +688,22 @@ version = "4.55.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/76/61/a300d1574dc381393424047c0396a0e213db212e28361123af9830d71a8d/fonttools-4.55.3.tar.gz", hash = "sha256:3983313c2a04d6cc1fe9251f8fc647754cf49a61dac6cb1e7249ae67afaafc45", size = 3498155 } wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/f3/9ac8c6705e4a0ff3c29e524df1caeee6f2987b02fb630129f21cc99a8212/fonttools-4.55.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1dcc07934a2165ccdc3a5a608db56fb3c24b609658a5b340aee4ecf3ba679dc0", size = 2769857 }, + { url = "https://files.pythonhosted.org/packages/d8/24/e8b8edd280bdb7d0ecc88a5d952b1dec2ee2335be71cc5a33c64871cdfe8/fonttools-4.55.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f7d66c15ba875432a2d2fb419523f5d3d347f91f48f57b8b08a2dfc3c39b8a3f", size = 2299705 }, + { url = "https://files.pythonhosted.org/packages/f8/9e/e1ba20bd3b71870207fd45ca3b90208a7edd8ae3b001081dc31c45adb017/fonttools-4.55.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e4ae3592e62eba83cd2c4ccd9462dcfa603ff78e09110680a5444c6925d841", size = 4576104 }, + { url = "https://files.pythonhosted.org/packages/34/db/d423bc646e6703fe3e6aea0edd22a2df47b9d188c5f7f1b49070be4d2205/fonttools-4.55.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d65a3022c35e404d19ca14f291c89cc5890032ff04f6c17af0bd1927299674", size = 4618282 }, + { url = "https://files.pythonhosted.org/packages/75/a0/e5062ac960a385b984ba74e7b55132e7f2c65e449e8330ab0f595407a3de/fonttools-4.55.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d342e88764fb201286d185093781bf6628bbe380a913c24adf772d901baa8276", size = 4570539 }, + { url = "https://files.pythonhosted.org/packages/1f/33/0d744ff518ebe50020b63e5018b8b278efd6a930c1d2eedda7defc42153b/fonttools-4.55.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dd68c87a2bfe37c5b33bcda0fba39b65a353876d3b9006fde3adae31f97b3ef5", size = 4742411 }, + { url = "https://files.pythonhosted.org/packages/7e/6c/2f768652dba6b801f1567fc5d1829cda369bcd6e95e315a91e628f91c702/fonttools-4.55.3-cp310-cp310-win32.whl", hash = "sha256:1bc7ad24ff98846282eef1cbeac05d013c2154f977a79886bb943015d2b1b261", size = 2175132 }, + { url = "https://files.pythonhosted.org/packages/19/d1/4dcd865360fb2c499749a913fe80e41c26e8ae18629d87dfffa3de27e831/fonttools-4.55.3-cp310-cp310-win_amd64.whl", hash = "sha256:b54baf65c52952db65df39fcd4820668d0ef4766c0ccdf32879b77f7c804d5c5", size = 2219430 }, + { url = "https://files.pythonhosted.org/packages/4b/18/14be25545600bd100e5b74a3ac39089b7c1cb403dc513b7ca348be3381bf/fonttools-4.55.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c4491699bad88efe95772543cd49870cf756b019ad56294f6498982408ab03e", size = 2771005 }, + { url = "https://files.pythonhosted.org/packages/b2/51/2e1a5d3871cd7c2ae2054b54e92604e7d6abc3fd3656e9583c399648fe1c/fonttools-4.55.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5323a22eabddf4b24f66d26894f1229261021dacd9d29e89f7872dd8c63f0b8b", size = 2300654 }, + { url = "https://files.pythonhosted.org/packages/73/1a/50109bb2703bc6f774b52ea081db21edf2a9fa4b6d7485faadf9d1b997e9/fonttools-4.55.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5480673f599ad410695ca2ddef2dfefe9df779a9a5cda89503881e503c9c7d90", size = 4877541 }, + { url = "https://files.pythonhosted.org/packages/5d/52/c0b9857fa075da1b8806c5dc2d8342918a8cc2065fd14fbddb3303282693/fonttools-4.55.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da9da6d65cd7aa6b0f806556f4985bcbf603bf0c5c590e61b43aa3e5a0f822d0", size = 4906304 }, + { url = "https://files.pythonhosted.org/packages/0b/1b/55f85c7e962d295e456d5209581c919620ee3e877b95cd86245187a5050f/fonttools-4.55.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e894b5bd60d9f473bed7a8f506515549cc194de08064d829464088d23097331b", size = 4888087 }, + { url = "https://files.pythonhosted.org/packages/83/13/6f2809c612ea2ac51391f92468ff861c63473601530fca96458b453212bf/fonttools-4.55.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aee3b57643827e237ff6ec6d28d9ff9766bd8b21e08cd13bff479e13d4b14765", size = 5056958 }, + { url = "https://files.pythonhosted.org/packages/c1/28/d0ea9e872fa4208b9dfca686e1dd9ca22f6c9ef33ecff2f0ebc2dbe7c29b/fonttools-4.55.3-cp311-cp311-win32.whl", hash = "sha256:eb6ca911c4c17eb51853143624d8dc87cdcdf12a711fc38bf5bd21521e79715f", size = 2173939 }, + { url = "https://files.pythonhosted.org/packages/be/36/d74ae1020bc41a1dff3e6f5a99f646563beecb97e386d27abdac3ba07650/fonttools-4.55.3-cp311-cp311-win_amd64.whl", hash = "sha256:6314bf82c54c53c71805318fcf6786d986461622dd926d92a465199ff54b1b72", size = 2220363 }, { url = "https://files.pythonhosted.org/packages/89/58/fbcf5dff7e3ea844bb00c4d806ca1e339e1f2dce5529633bf4842c0c9a1f/fonttools-4.55.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9e736f60f4911061235603a6119e72053073a12c6d7904011df2d8fad2c0e35", size = 2765380 }, { url = "https://files.pythonhosted.org/packages/81/dd/da6e329e51919b4f421c8738f3497e2ab08c168e76aaef7b6d5351862bdf/fonttools-4.55.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a8aa2c5e5b8b3bcb2e4538d929f6589a5c6bdb84fd16e2ed92649fb5454f11c", size = 2297940 }, { url = "https://files.pythonhosted.org/packages/00/44/f5ee560858425c99ef07e04919e736db09d6416408e5a8d3bbfb4a6623fd/fonttools-4.55.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07f8288aacf0a38d174445fc78377a97fb0b83cfe352a90c9d9c1400571963c7", size = 4793327 }, @@ -562,21 +723,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/3b/406d17b1f63e04a82aa621936e6e1c53a8c05458abd66300ac85ea7f9ae9/fonttools-4.55.3-py3-none-any.whl", hash = "sha256:f412604ccbeee81b091b420272841e5ec5ef68967a9790e80bffd0e30b8e2977", size = 1111638 }, ] -[[package]] -name = "furo" -version = "2024.8.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "pygments" }, - { name = "sphinx" }, - { name = "sphinx-basic-ng" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a0/e2/d351d69a9a9e4badb4a5be062c2d0e87bd9e6c23b5e57337fef14bef34c8/furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01", size = 1661506 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/48/e791a7ed487dbb9729ef32bb5d1af16693d8925f4366befef54119b2e576/furo-2024.8.6-py3-none-any.whl", hash = "sha256:6cd97c58b47813d3619e63e9081169880fbe331f0ca883c871ff1f3f11814f5c", size = 341333 }, -] - [[package]] name = "ghp-import" version = "2.1.0" @@ -595,6 +741,24 @@ version = "3.1.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } wheels = [ + { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, + { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, + { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, + { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, + { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, + { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, + { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, + { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, + { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, + { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 }, + { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 }, + { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 }, + { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 }, + { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 }, + { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 }, + { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 }, + { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 }, + { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 }, { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, @@ -652,15 +816,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] -[[package]] -name = "imagesize" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, -] - [[package]] name = "iniconfig" version = "2.0.0" @@ -701,6 +856,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "decorator" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "jedi" }, { name = "matplotlib-inline" }, { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, @@ -708,6 +864,7 @@ dependencies = [ { name = "pygments" }, { name = "stack-data" }, { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d8/8b/710af065ab8ed05649afa5bd1e07401637c9ec9fb7cfda9eac7e91e9fbd4/ipython-8.30.0.tar.gz", hash = "sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e", size = 5592205 } wheels = [ @@ -783,6 +940,38 @@ version = "1.4.7" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/85/4d/2255e1c76304cbd60b48cee302b66d1dde4468dc5b1160e4b7cb43778f2a/kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", size = 97286 } wheels = [ + { url = "https://files.pythonhosted.org/packages/97/14/fc943dd65268a96347472b4fbe5dcc2f6f55034516f80576cd0dd3a8930f/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6", size = 122440 }, + { url = "https://files.pythonhosted.org/packages/1e/46/e68fed66236b69dd02fcdb506218c05ac0e39745d696d22709498896875d/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17", size = 65758 }, + { url = "https://files.pythonhosted.org/packages/ef/fa/65de49c85838681fc9cb05de2a68067a683717321e01ddafb5b8024286f0/kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9", size = 64311 }, + { url = "https://files.pythonhosted.org/packages/42/9c/cc8d90f6ef550f65443bad5872ffa68f3dee36de4974768628bea7c14979/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9", size = 1637109 }, + { url = "https://files.pythonhosted.org/packages/55/91/0a57ce324caf2ff5403edab71c508dd8f648094b18cfbb4c8cc0fde4a6ac/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c", size = 1617814 }, + { url = "https://files.pythonhosted.org/packages/12/5d/c36140313f2510e20207708adf36ae4919416d697ee0236b0ddfb6fd1050/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599", size = 1400881 }, + { url = "https://files.pythonhosted.org/packages/56/d0/786e524f9ed648324a466ca8df86298780ef2b29c25313d9a4f16992d3cf/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05", size = 1512972 }, + { url = "https://files.pythonhosted.org/packages/67/5a/77851f2f201e6141d63c10a0708e996a1363efaf9e1609ad0441b343763b/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407", size = 1444787 }, + { url = "https://files.pythonhosted.org/packages/06/5f/1f5eaab84355885e224a6fc8d73089e8713dc7e91c121f00b9a1c58a2195/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278", size = 2199212 }, + { url = "https://files.pythonhosted.org/packages/b5/28/9152a3bfe976a0ae21d445415defc9d1cd8614b2910b7614b30b27a47270/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5", size = 2346399 }, + { url = "https://files.pythonhosted.org/packages/26/f6/453d1904c52ac3b400f4d5e240ac5fec25263716723e44be65f4d7149d13/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad", size = 2308688 }, + { url = "https://files.pythonhosted.org/packages/5a/9a/d4968499441b9ae187e81745e3277a8b4d7c60840a52dc9d535a7909fac3/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895", size = 2445493 }, + { url = "https://files.pythonhosted.org/packages/07/c9/032267192e7828520dacb64dfdb1d74f292765f179e467c1cba97687f17d/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3", size = 2262191 }, + { url = "https://files.pythonhosted.org/packages/6c/ad/db0aedb638a58b2951da46ddaeecf204be8b4f5454df020d850c7fa8dca8/kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc", size = 46644 }, + { url = "https://files.pythonhosted.org/packages/12/ca/d0f7b7ffbb0be1e7c2258b53554efec1fd652921f10d7d85045aff93ab61/kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c", size = 55877 }, + { url = "https://files.pythonhosted.org/packages/97/6c/cfcc128672f47a3e3c0d918ecb67830600078b025bfc32d858f2e2d5c6a4/kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a", size = 48347 }, + { url = "https://files.pythonhosted.org/packages/e9/44/77429fa0a58f941d6e1c58da9efe08597d2e86bf2b2cce6626834f49d07b/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54", size = 122442 }, + { url = "https://files.pythonhosted.org/packages/e5/20/8c75caed8f2462d63c7fd65e16c832b8f76cda331ac9e615e914ee80bac9/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95", size = 65762 }, + { url = "https://files.pythonhosted.org/packages/f4/98/fe010f15dc7230f45bc4cf367b012d651367fd203caaa992fd1f5963560e/kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935", size = 64319 }, + { url = "https://files.pythonhosted.org/packages/8b/1b/b5d618f4e58c0675654c1e5051bcf42c776703edb21c02b8c74135541f60/kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb", size = 1334260 }, + { url = "https://files.pythonhosted.org/packages/b8/01/946852b13057a162a8c32c4c8d2e9ed79f0bb5d86569a40c0b5fb103e373/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02", size = 1426589 }, + { url = "https://files.pythonhosted.org/packages/70/d1/c9f96df26b459e15cf8a965304e6e6f4eb291e0f7a9460b4ad97b047561e/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51", size = 1541080 }, + { url = "https://files.pythonhosted.org/packages/d3/73/2686990eb8b02d05f3de759d6a23a4ee7d491e659007dd4c075fede4b5d0/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052", size = 1470049 }, + { url = "https://files.pythonhosted.org/packages/a7/4b/2db7af3ed3af7c35f388d5f53c28e155cd402a55432d800c543dc6deb731/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18", size = 1426376 }, + { url = "https://files.pythonhosted.org/packages/05/83/2857317d04ea46dc5d115f0df7e676997bbd968ced8e2bd6f7f19cfc8d7f/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545", size = 2222231 }, + { url = "https://files.pythonhosted.org/packages/0d/b5/866f86f5897cd4ab6d25d22e403404766a123f138bd6a02ecb2cdde52c18/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b", size = 2368634 }, + { url = "https://files.pythonhosted.org/packages/c1/ee/73de8385403faba55f782a41260210528fe3273d0cddcf6d51648202d6d0/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36", size = 2329024 }, + { url = "https://files.pythonhosted.org/packages/a1/e7/cd101d8cd2cdfaa42dc06c433df17c8303d31129c9fdd16c0ea37672af91/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3", size = 2468484 }, + { url = "https://files.pythonhosted.org/packages/e1/72/84f09d45a10bc57a40bb58b81b99d8f22b58b2040c912b7eb97ebf625bf2/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523", size = 2284078 }, + { url = "https://files.pythonhosted.org/packages/d2/d4/71828f32b956612dc36efd7be1788980cb1e66bfb3706e6dec9acad9b4f9/kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d", size = 46645 }, + { url = "https://files.pythonhosted.org/packages/a1/65/d43e9a20aabcf2e798ad1aff6c143ae3a42cf506754bcb6a7ed8259c8425/kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b", size = 56022 }, + { url = "https://files.pythonhosted.org/packages/35/b3/9f75a2e06f1b4ca00b2b192bc2b739334127d27f1d0625627ff8479302ba/kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376", size = 48536 }, { url = "https://files.pythonhosted.org/packages/97/9c/0a11c714cf8b6ef91001c8212c4ef207f772dd84540104952c45c1f0a249/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2", size = 121808 }, { url = "https://files.pythonhosted.org/packages/f2/d8/0fe8c5f5d35878ddd135f44f2af0e4e1d379e1c7b0716f97cdcb88d4fd27/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a", size = 65531 }, { url = "https://files.pythonhosted.org/packages/80/c5/57fa58276dfdfa612241d640a64ca2f76adc6ffcebdbd135b4ef60095098/kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee", size = 63894 }, @@ -815,6 +1004,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a8/b2/15f7f556df0a6e5b3772a1e076a9d9f6c538ce5f05bd590eca8106508e06/kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", size = 46364 }, { url = "https://files.pythonhosted.org/packages/0b/db/32e897e43a330eee8e4770bfd2737a9584b23e33587a0812b8e20aac38f7/kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", size = 55887 }, { url = "https://files.pythonhosted.org/packages/c8/a4/df2bdca5270ca85fd25253049eb6708d4127be2ed0e5c2650217450b59e9/kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", size = 48530 }, + { url = "https://files.pythonhosted.org/packages/ac/59/741b79775d67ab67ced9bb38552da688c0305c16e7ee24bba7a2be253fb7/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643", size = 59491 }, + { url = "https://files.pythonhosted.org/packages/58/cc/fb239294c29a5656e99e3527f7369b174dd9cc7c3ef2dea7cb3c54a8737b/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706", size = 57648 }, + { url = "https://files.pythonhosted.org/packages/3b/ef/2f009ac1f7aab9f81efb2d837301d255279d618d27b6015780115ac64bdd/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6", size = 84257 }, + { url = "https://files.pythonhosted.org/packages/81/e1/c64f50987f85b68b1c52b464bb5bf73e71570c0f7782d626d1eb283ad620/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2", size = 80906 }, + { url = "https://files.pythonhosted.org/packages/fd/71/1687c5c0a0be2cee39a5c9c389e546f9c6e215e46b691d00d9f646892083/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4", size = 79951 }, + { url = "https://files.pythonhosted.org/packages/ea/8b/d7497df4a1cae9367adf21665dd1f896c2a7aeb8769ad77b662c5e2bcce7/kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a", size = 55715 }, ] [[package]] @@ -826,24 +1021,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, ] -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, -] - [[package]] name = "markupsafe" version = "3.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, @@ -893,6 +1096,18 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/68/dd/fa2e1a45fce2d09f4aea3cee169760e672c8262325aa5796c49d543dc7e6/matplotlib-3.10.0.tar.gz", hash = "sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278", size = 36686418 } wheels = [ + { url = "https://files.pythonhosted.org/packages/09/ec/3cdff7b5239adaaacefcc4f77c316dfbbdf853c4ed2beec467e0fec31b9f/matplotlib-3.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2c5829a5a1dd5a71f0e31e6e8bb449bc0ee9dbfb05ad28fc0c6b55101b3a4be6", size = 8160551 }, + { url = "https://files.pythonhosted.org/packages/41/f2/b518f2c7f29895c9b167bf79f8529c63383ae94eaf49a247a4528e9a148d/matplotlib-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2a43cbefe22d653ab34bb55d42384ed30f611bcbdea1f8d7f431011a2e1c62e", size = 8034853 }, + { url = "https://files.pythonhosted.org/packages/ed/8d/45754b4affdb8f0d1a44e4e2bcd932cdf35b256b60d5eda9f455bb293ed0/matplotlib-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:607b16c8a73943df110f99ee2e940b8a1cbf9714b65307c040d422558397dac5", size = 8446724 }, + { url = "https://files.pythonhosted.org/packages/09/5a/a113495110ae3e3395c72d82d7bc4802902e46dc797f6b041e572f195c56/matplotlib-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01d2b19f13aeec2e759414d3bfe19ddfb16b13a1250add08d46d5ff6f9be83c6", size = 8583905 }, + { url = "https://files.pythonhosted.org/packages/12/b1/8b1655b4c9ed4600c817c419f7eaaf70082630efd7556a5b2e77a8a3cdaf/matplotlib-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e6c6461e1fc63df30bf6f80f0b93f5b6784299f721bc28530477acd51bfc3d1", size = 9395223 }, + { url = "https://files.pythonhosted.org/packages/5a/85/b9a54d64585a6b8737a78a61897450403c30f39e0bd3214270bb0b96f002/matplotlib-3.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:994c07b9d9fe8d25951e3202a68c17900679274dadfc1248738dcfa1bd40d7f3", size = 8025355 }, + { url = "https://files.pythonhosted.org/packages/0c/f1/e37f6c84d252867d7ddc418fff70fc661cfd363179263b08e52e8b748e30/matplotlib-3.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:fd44fc75522f58612ec4a33958a7e5552562b7705b42ef1b4f8c0818e304a363", size = 8171677 }, + { url = "https://files.pythonhosted.org/packages/c7/8b/92e9da1f28310a1f6572b5c55097b0c0ceb5e27486d85fb73b54f5a9b939/matplotlib-3.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c58a9622d5dbeb668f407f35f4e6bfac34bb9ecdcc81680c04d0258169747997", size = 8044945 }, + { url = "https://files.pythonhosted.org/packages/c5/cb/49e83f0fd066937a5bd3bc5c5d63093703f3637b2824df8d856e0558beef/matplotlib-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:845d96568ec873be63f25fa80e9e7fae4be854a66a7e2f0c8ccc99e94a8bd4ef", size = 8458269 }, + { url = "https://files.pythonhosted.org/packages/b2/7d/2d873209536b9ee17340754118a2a17988bc18981b5b56e6715ee07373ac/matplotlib-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5439f4c5a3e2e8eab18e2f8c3ef929772fd5641876db71f08127eed95ab64683", size = 8599369 }, + { url = "https://files.pythonhosted.org/packages/b8/03/57d6cbbe85c61fe4cbb7c94b54dce443d68c21961830833a1f34d056e5ea/matplotlib-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4673ff67a36152c48ddeaf1135e74ce0d4bce1bbf836ae40ed39c29edf7e2765", size = 9405992 }, + { url = "https://files.pythonhosted.org/packages/14/cf/e382598f98be11bf51dd0bc60eca44a517f6793e3dc8b9d53634a144620c/matplotlib-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e8632baebb058555ac0cde75db885c61f1212e47723d63921879806b40bec6a", size = 8034580 }, { url = "https://files.pythonhosted.org/packages/44/c7/6b2d8cb7cc251d53c976799cacd3200add56351c175ba89ab9cbd7c1e68a/matplotlib-3.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4659665bc7c9b58f8c00317c3c2a299f7f258eeae5a5d56b4c64226fca2f7c59", size = 8172465 }, { url = "https://files.pythonhosted.org/packages/42/2a/6d66d0fba41e13e9ca6512a0a51170f43e7e7ed3a8dfa036324100775612/matplotlib-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d44cb942af1693cced2604c33a9abcef6205601c445f6d0dc531d813af8a2f5a", size = 8043300 }, { url = "https://files.pythonhosted.org/packages/90/60/2a60342b27b90a16bada939a85e29589902b41073f59668b904b15ea666c/matplotlib-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a994f29e968ca002b50982b27168addfd65f0105610b6be7fa515ca4b5307c95", size = 8448936 }, @@ -911,6 +1126,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c2/2d/b5949fb2b76e9b47ab05e25a5f5f887c70de20d8b0cbc704a4e2ee71c786/matplotlib-3.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d907fddb39f923d011875452ff1eca29a9e7f21722b873e90db32e5d8ddff12e", size = 8610334 }, { url = "https://files.pythonhosted.org/packages/d6/9a/6e3c799d5134d9af44b01c787e1360bee38cf51850506ea2e743a787700b/matplotlib-3.10.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3b427392354d10975c1d0f4ee18aa5844640b512d5311ef32efd4dd7db106ede", size = 9406777 }, { url = "https://files.pythonhosted.org/packages/0e/dd/e6ae97151e5ed648ab2ea48885bc33d39202b640eec7a2910e2c843f7ac0/matplotlib-3.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5fd41b0ec7ee45cd960a8e71aea7c946a28a0b8a4dcee47d2856b2af051f334c", size = 8109742 }, + { url = "https://files.pythonhosted.org/packages/32/5f/29def7ce4e815ab939b56280976ee35afffb3bbdb43f332caee74cb8c951/matplotlib-3.10.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:81713dd0d103b379de4516b861d964b1d789a144103277769238c732229d7f03", size = 8155500 }, + { url = "https://files.pythonhosted.org/packages/de/6d/d570383c9f7ca799d0a54161446f9ce7b17d6c50f2994b653514bcaa108f/matplotlib-3.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:359f87baedb1f836ce307f0e850d12bb5f1936f70d035561f90d41d305fdacea", size = 8032398 }, + { url = "https://files.pythonhosted.org/packages/c9/b4/680aa700d99b48e8c4393fa08e9ab8c49c0555ee6f4c9c0a5e8ea8dfde5d/matplotlib-3.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae80dc3a4add4665cf2faa90138384a7ffe2a4e37c58d83e115b54287c4f06ef", size = 8587361 }, ] [[package]] @@ -925,27 +1143,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, ] -[[package]] -name = "mdit-py-plugins" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, -] - [[package]] name = "mergedeep" version = "1.3.4" @@ -1077,16 +1274,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/c1/ac524e1026d9580cbc654b5d19f5843c8b364a66d30f956372cd09fd2f92/mkdocstrings_python-1.12.2-py3-none-any.whl", hash = "sha256:7f7d40d6db3cb1f5d19dbcd80e3efe4d0ba32b073272c0c0de9de2e604eda62a", size = 111759 }, ] +[[package]] +name = "multiurl" +version = "0.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/db/aad981174d3bdaecc1d7e1f2d176641f022300ddf3a17b13c2775d041b7a/multiurl-0.3.3.tar.gz", hash = "sha256:f4d0b69dcf4a0ed740daa313dbcd4d5665420d305c50ca879285e96dc828093f", size = 18382 } + [[package]] name = "mypy" version = "1.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/8c/206de95a27722b5b5a8c85ba3100467bd86299d92a4f71c6b9aa448bfa2f/mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", size = 11020731 }, + { url = "https://files.pythonhosted.org/packages/ab/bb/b31695a29eea76b1569fd28b4ab141a1adc9842edde080d1e8e1776862c7/mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", size = 10184276 }, + { url = "https://files.pythonhosted.org/packages/a5/2d/4a23849729bb27934a0e079c9c1aad912167d875c7b070382a408d459651/mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", size = 12587706 }, + { url = "https://files.pythonhosted.org/packages/5c/c3/d318e38ada50255e22e23353a469c791379825240e71b0ad03e76ca07ae6/mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", size = 13105586 }, + { url = "https://files.pythonhosted.org/packages/4a/25/3918bc64952370c3dbdbd8c82c363804678127815febd2925b7273d9482c/mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", size = 9632318 }, + { url = "https://files.pythonhosted.org/packages/d0/19/de0822609e5b93d02579075248c7aa6ceaddcea92f00bf4ea8e4c22e3598/mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", size = 10939027 }, + { url = "https://files.pythonhosted.org/packages/c8/71/6950fcc6ca84179137e4cbf7cf41e6b68b4a339a1f5d3e954f8c34e02d66/mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", size = 10108699 }, + { url = "https://files.pythonhosted.org/packages/26/50/29d3e7dd166e74dc13d46050b23f7d6d7533acf48f5217663a3719db024e/mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", size = 12506263 }, + { url = "https://files.pythonhosted.org/packages/3f/1d/676e76f07f7d5ddcd4227af3938a9c9640f293b7d8a44dd4ff41d4db25c1/mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", size = 12984688 }, + { url = "https://files.pythonhosted.org/packages/9c/03/5a85a30ae5407b1d28fab51bd3e2103e52ad0918d1e68f02a7778669a307/mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", size = 9626811 }, { url = "https://files.pythonhosted.org/packages/fb/31/c526a7bd2e5c710ae47717c7a5f53f616db6d9097caf48ad650581e81748/mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", size = 11077900 }, { url = "https://files.pythonhosted.org/packages/83/67/b7419c6b503679d10bd26fc67529bc6a1f7a5f220bbb9f292dc10d33352f/mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", size = 10074818 }, { url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 }, @@ -1109,23 +1329,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, ] -[[package]] -name = "myst-parser" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "jinja2" }, - { name = "markdown-it-py" }, - { name = "mdit-py-plugins" }, - { name = "pyyaml" }, - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 }, -] - [[package]] name = "nest-asyncio" version = "1.6.0" @@ -1141,6 +1344,26 @@ version = "2.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/47/1b/1d565e0f6e156e1522ab564176b8b29d71e13d8caf003a08768df3d5cec5/numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0", size = 20225497 } wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/81/3882353e097204fe4d7a5fe026b694b0104b78f930c969faadeed1538e00/numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa", size = 21212476 }, + { url = "https://files.pythonhosted.org/packages/2c/64/5577dc71240272749e07fcacb47c0f29e31ba4fbd1613fefbd1aa88efc29/numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219", size = 14351441 }, + { url = "https://files.pythonhosted.org/packages/c9/43/850c040481c19c1c2289203a606df1a202eeb3aa81440624bac891024f83/numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e", size = 5390304 }, + { url = "https://files.pythonhosted.org/packages/73/96/a4c8a86300dbafc7e4f44d8986f8b64950b7f4640a2dc5c91e036afe28c6/numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9", size = 6925476 }, + { url = "https://files.pythonhosted.org/packages/0c/0a/22129c3107c4fb237f97876df4399a5c3a83f3d95f86e0353ae6fbbd202f/numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3", size = 14329997 }, + { url = "https://files.pythonhosted.org/packages/4c/49/c2adeccc8a47bcd9335ec000dfcb4de34a7c34aeaa23af57cd504017e8c3/numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83", size = 16378908 }, + { url = "https://files.pythonhosted.org/packages/8d/85/b65f4596748cc5468c0a978a16b3be45f6bcec78339b0fe7bce71d121d89/numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a", size = 15540949 }, + { url = "https://files.pythonhosted.org/packages/ff/b3/3b18321c94a6a6a1d972baf1b39a6de50e65c991002c014ffbcce7e09be8/numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31", size = 18167677 }, + { url = "https://files.pythonhosted.org/packages/41/f0/fa2a76e893a05764e4474f6011575c4e4ccf32af9c95bfcc8ef4b8a99f69/numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661", size = 6570288 }, + { url = "https://files.pythonhosted.org/packages/97/4e/0b7debcd013214db224997b0d3e39bb7b3656d37d06dfc31bb57d42d143b/numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4", size = 12912730 }, + { url = "https://files.pythonhosted.org/packages/80/1b/736023977a96e787c4e7653a1ac2d31d4f6ab6b4048f83c8359f7c0af2e3/numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6", size = 21216607 }, + { url = "https://files.pythonhosted.org/packages/85/4f/5f0be4c5c93525e663573bab9e29bd88a71f85de3a0d01413ee05bce0c2f/numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90", size = 14387756 }, + { url = "https://files.pythonhosted.org/packages/36/78/c38af7833c4f29999cdacdf12452b43b660cd25a1990ea9a7edf1fb01f17/numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608", size = 5388483 }, + { url = "https://files.pythonhosted.org/packages/e9/b5/306ac6ee3f8f0c51abd3664ee8a9b8e264cbf179a860674827151ecc0a9c/numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da", size = 6929721 }, + { url = "https://files.pythonhosted.org/packages/ea/15/e33a7d86d8ce91de82c34ce94a87f2b8df891e603675e83ec7039325ff10/numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74", size = 14334667 }, + { url = "https://files.pythonhosted.org/packages/52/33/10825f580f42a353f744abc450dcd2a4b1e6f1931abb0ccbd1d63bd3993c/numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e", size = 16390204 }, + { url = "https://files.pythonhosted.org/packages/b4/24/36cce77559572bdc6c8bcdd2f3e0db03c7079d14b9a1cd342476d7f451e8/numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b", size = 15556123 }, + { url = "https://files.pythonhosted.org/packages/05/51/2d706d14adee8f5c70c5de3831673d4d57051fc9ac6f3f6bff8811d2f9bd/numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d", size = 18179898 }, + { url = "https://files.pythonhosted.org/packages/8a/e7/ea8b7652564113f218e75b296e3545a256d88b233021f792fd08591e8f33/numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410", size = 6568146 }, + { url = "https://files.pythonhosted.org/packages/d0/06/3d1ff6ed377cb0340baf90487a35f15f9dc1db8e0a07de2bf2c54a8e490f/numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73", size = 12916677 }, { url = "https://files.pythonhosted.org/packages/7f/bc/a20dc4e1d051149052762e7647455311865d11c603170c476d1e910a353e/numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3", size = 20909153 }, { url = "https://files.pythonhosted.org/packages/60/3d/ac4fb63f36db94f4c7db05b45e3ecb3f88f778ca71850664460c78cfde41/numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e", size = 14095021 }, { url = "https://files.pythonhosted.org/packages/41/6d/a654d519d24e4fcc7a83d4a51209cda086f26cf30722b3d8ffc1aa9b775e/numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67", size = 5125491 }, @@ -1171,19 +1394,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/65/d9/dddf398b2b6c5d750892a207a469c2854a8db0f033edaf72103af8cf05aa/numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72", size = 17851668 }, { url = "https://files.pythonhosted.org/packages/d4/dc/09a4e5819a9782a213c0eb4eecacdc1cd75ad8dac99279b04cfccb7eeb0a/numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066", size = 6325288 }, { url = "https://files.pythonhosted.org/packages/ce/e1/e0d06ec34036c92b43aef206efe99a5f5f04e12c776eab82a36e00c40afc/numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881", size = 12692303 }, -] - -[[package]] -name = "numpydoc" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, - { name = "tabulate" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ee/59/5d1d1afb0b9598e21e7cda477935188e39ef845bcf59cb65ac20845bfd45/numpydoc-1.8.0.tar.gz", hash = "sha256:022390ab7464a44f8737f79f8b31ce1d3cfa4b4af79ccaa1aac5e8368db587fb", size = 90445 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/45/56d99ba9366476cd8548527667f01869279cedb9e66b28eb4dfb27701679/numpydoc-1.8.0-py3-none-any.whl", hash = "sha256:72024c7fd5e17375dec3608a27c03303e8ad00c81292667955c6fea7a3ccf541", size = 64003 }, + { url = "https://files.pythonhosted.org/packages/f3/18/6d4e1274f221073058b621f4df8050958b7564b24b4fa25be9f1b7639274/numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773", size = 21043901 }, + { url = "https://files.pythonhosted.org/packages/19/3e/2b20599e7ead7ae1b89a77bb34f88c5ec12e43fbb320576ed646388d2cb7/numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e", size = 6789122 }, + { url = "https://files.pythonhosted.org/packages/c9/5a/378954132c192fafa6c3d5c160092a427c7562e5bda0cc6ad9cc37008a7a/numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7", size = 16194018 }, + { url = "https://files.pythonhosted.org/packages/67/17/209bda34fc83f3436834392f44643e66dcf3c77465f232102e7f1c7d8eae/numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221", size = 12819486 }, ] [[package]] @@ -1232,6 +1446,20 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827 }, + { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897 }, + { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908 }, + { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210 }, + { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292 }, + { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379 }, + { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471 }, + { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222 }, + { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274 }, + { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836 }, + { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505 }, + { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420 }, + { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457 }, + { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166 }, { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 }, { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 }, { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 }, @@ -1290,6 +1518,28 @@ version = "10.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, + { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265 }, + { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655 }, + { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304 }, + { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804 }, + { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126 }, + { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541 }, + { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616 }, + { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802 }, + { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213 }, + { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498 }, + { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219 }, { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350 }, { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980 }, { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799 }, @@ -1312,6 +1562,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603 }, { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972 }, { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375 }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, ] [[package]] @@ -1426,6 +1683,18 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/47/c2/0572c8e31aebf0270f15f3368adebd10fc473de9f09567a0743a3bc41c8d/pyproj-3.7.0.tar.gz", hash = "sha256:bf658f4aaf815d9d03c8121650b6f0b8067265c36e31bc6660b98ef144d81813", size = 225577 } wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/fa/8a769da6bb8e26b1028c19d048b88373a40bd8e17a893e07b9889d1aed03/pyproj-3.7.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d5c7e7d24b967e328a5efd013f466804a1f226d1106ac7efc47dcc99360dbc8f", size = 6270121 }, + { url = "https://files.pythonhosted.org/packages/82/65/ee312dc4cdd2499cc5984144e05c582604afd76ba01289d89d74b50ab654/pyproj-3.7.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:448958c46bd3fe2da91c89ba551ac5835e63073ca861422c6eb1af89979dfab1", size = 4633387 }, + { url = "https://files.pythonhosted.org/packages/f8/0d/d300194f021e3d56b30bb45bd19447bb00761c62f5342371bd389b774f82/pyproj-3.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f673ca345bb92afc93d4235938ca0c9a76237aa7addf42a95965c8dc8cad9b49", size = 6330358 }, + { url = "https://files.pythonhosted.org/packages/52/30/c82c12cea9a5c17fac146212cd914ec587f646eef91986dcb7f629fe0f7f/pyproj-3.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee60895f60cbd1a9c903ab2bc22adea63004296a1c28b8775a11cf50905cf085", size = 9227537 }, + { url = "https://files.pythonhosted.org/packages/09/94/34bd5a5e637e8839beb17cc09410785bad24098ef01e52f66fe32bcf74fa/pyproj-3.7.0-cp310-cp310-win32.whl", hash = "sha256:0dd31b0740ee010934234f848d2d092c66146cb8d0ba009a64e41d192caa7686", size = 5822094 }, + { url = "https://files.pythonhosted.org/packages/9e/04/33835c6ca0edf266b56495555c375994c42f914eb65a639b363d6f2c47f9/pyproj-3.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:7943d85ba39e89c51b920339ff63162d63bf89da161f0acb6206b0d39b11661e", size = 6230436 }, + { url = "https://files.pythonhosted.org/packages/e2/8f/15ff6ab10a08050e94afcd544962a1a930d0bb7ca102ad39795a847eb340/pyproj-3.7.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:e66d8d42dbdf232e121546c5a1dec097caf0454e4885c09a8e03cdcee0753c03", size = 6272213 }, + { url = "https://files.pythonhosted.org/packages/2d/4d/610fe2a17de71b4fe210af69ce25f2d65379ba0a48299129894d0d0988ee/pyproj-3.7.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7764b64a0aefe40134a2828b3a40be88f6c8b7832c45d8a9f2bd592ace4b2a3b", size = 4634548 }, + { url = "https://files.pythonhosted.org/packages/d6/27/0327d0b0fcdfc4cf72696a2effca2963e524dcd846a0274ba503f8bf2648/pyproj-3.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53c442c5081dc95346996f5c4323fde2caafc69c6e60b4707aa46e88244f1e04", size = 6333913 }, + { url = "https://files.pythonhosted.org/packages/3c/e5/2cb256148c730b9c3f74bfb3c03904f5070499c6dcaea153073a9642c6c6/pyproj-3.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc5b305d4d5d7697885681d9b660623e328227612823d5c660e0a9566cb48838", size = 9460363 }, + { url = "https://files.pythonhosted.org/packages/ba/a3/4aa1e8e78ad18aa170efd2c94c1931bf2a34c526683b874d06e40fa323f6/pyproj-3.7.0-cp311-cp311-win32.whl", hash = "sha256:de2b47d748dc41cccb6b3b713d4d7dc9aa1046a82141c8665026908726426abc", size = 5820551 }, + { url = "https://files.pythonhosted.org/packages/26/0c/b084e8839a117eaad8cb4fbaa81bbb24c6f183de0ee95c6c4e2770ab6f09/pyproj-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:38cba7c4c5679e40242dd959133e95b908d3b912dd66291094fd13510e8517ff", size = 6231788 }, { url = "https://files.pythonhosted.org/packages/bd/19/be806b711e9ebfb80411c653054157db128fffdd7f8493e3064136c8d880/pyproj-3.7.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:8cbec92bdd6e9933ca08795c12717d1384e9b51cf4b1acf0d753db255a75c51e", size = 6261400 }, { url = "https://files.pythonhosted.org/packages/99/3b/8497995e8cae0049d013679c6a7ac6c57b816d590c733a388748dafe5af5/pyproj-3.7.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8c4a8e4d3ba76c3adac3c087544cf92f7f9a19ea34946904a13fca48cc1c0106", size = 4637848 }, { url = "https://files.pythonhosted.org/packages/ea/f7/2a5b46d6f8da913d58d44942ab06ca4803b5424b73259b15344cf90040f6/pyproj-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82624fb42aa31f6b1a860fbc0316babd07fd712642bc31022df4e9b4056bf463", size = 6324856 }, @@ -1455,15 +1724,30 @@ version = "8.3.4" 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 = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } wheels = [ { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, ] +[[package]] +name = "pytest-cov" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1490,15 +1774,15 @@ wheels = [ [[package]] name = "python-socketio" -version = "5.11.4" +version = "5.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bidict" }, { name = "python-engineio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/31/4ba0d9d86c645ba335d645f49167ca58b0874ca0e421682f97964e8adb42/python_socketio-5.11.4.tar.gz", hash = "sha256:8b0b8ff2964b2957c865835e936310190639c00310a47d77321a594d1665355e", size = 118982 } +sdist = { url = "https://files.pythonhosted.org/packages/95/35/554914000c4549f88d45ddb9fd616f380d53afcd11c6a82f8acb858b3419/python_socketio-5.12.0.tar.gz", hash = "sha256:39b55bff4ef6ac5c39b8bbc38fa61962e22e15349b038c1ca7ee2e18824e06dc", size = 120093 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/9a/52b94c8c9516e07844d3da3d0da3e68649f172aeeace8d7a1becca9e6111/python_socketio-5.11.4-py3-none-any.whl", hash = "sha256:42efaa3e3e0b166fc72a527488a13caaac2cefc76174252486503bd496284945", size = 76246 }, + { url = "https://files.pythonhosted.org/packages/f7/5f/bccb8d210f9a2bb5d5723cfe3034d940501045f8c1e074ec3c2574d2519c/python_socketio-5.12.0-py3-none-any.whl", hash = "sha256:50fe22fd2b0aa634df3e74489e42217b09af2fb22eee45f2c006df36d1d08cb9", size = 77036 }, ] [[package]] @@ -1515,6 +1799,12 @@ name = "pywin32" version = "308" source = { registry = "https://pypi.org/simple" } wheels = [ + { url = "https://files.pythonhosted.org/packages/72/a6/3e9f2c474895c1bb61b11fa9640be00067b5c5b363c501ee9c3fa53aec01/pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e", size = 5927028 }, + { url = "https://files.pythonhosted.org/packages/d9/b4/84e2463422f869b4b718f79eb7530a4c1693e96b8a4e5e968de38be4d2ba/pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e", size = 6558484 }, + { url = "https://files.pythonhosted.org/packages/9f/8f/fb84ab789713f7c6feacaa08dad3ec8105b88ade8d1c4f0f0dfcaaa017d6/pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c", size = 7971454 }, + { url = "https://files.pythonhosted.org/packages/eb/e2/02652007469263fe1466e98439831d65d4ca80ea1a2df29abecedf7e47b7/pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a", size = 5928156 }, + { url = "https://files.pythonhosted.org/packages/48/ef/f4fb45e2196bc7ffe09cad0542d9aff66b0e33f6c0954b43e49c33cad7bd/pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b", size = 6559559 }, + { url = "https://files.pythonhosted.org/packages/79/ef/68bb6aa865c5c9b11a35771329e95917b5559845bd75b65549407f9fc6b4/pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6", size = 7972495 }, { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 }, { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 }, { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 }, @@ -1529,6 +1819,24 @@ 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 } 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, { 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 }, { 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 }, { 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 }, @@ -1570,6 +1878,30 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/fd/05/bed626b9f7bb2322cdbbf7b4bd8f54b1b617b0d2ab2d3547d6e39428a48e/pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f", size = 271975 } wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/a8/9837c39aba390eb7d01924ace49d761c8dbe7bc2d6082346d00c8332e431/pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629", size = 1340058 }, + { url = "https://files.pythonhosted.org/packages/a2/1f/a006f2e8e4f7d41d464272012695da17fb95f33b54342612a6890da96ff6/pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b", size = 1008818 }, + { url = "https://files.pythonhosted.org/packages/b6/09/b51b6683fde5ca04593a57bbe81788b6b43114d8f8ee4e80afc991e14760/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764", size = 673199 }, + { url = "https://files.pythonhosted.org/packages/c9/78/486f3e2e824f3a645238332bf5a4c4b4477c3063033a27c1e4052358dee2/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c", size = 911762 }, + { url = "https://files.pythonhosted.org/packages/5e/3b/2eb1667c9b866f53e76ee8b0c301b0469745a23bd5a87b7ee3d5dd9eb6e5/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a", size = 868773 }, + { url = "https://files.pythonhosted.org/packages/16/29/ca99b4598a9dc7e468b5417eda91f372b595be1e3eec9b7cbe8e5d3584e8/pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88", size = 868834 }, + { url = "https://files.pythonhosted.org/packages/ad/e5/9efaeb1d2f4f8c50da04144f639b042bc52869d3a206d6bf672ab3522163/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f", size = 1202861 }, + { url = "https://files.pythonhosted.org/packages/c3/62/c721b5608a8ac0a69bb83cbb7d07a56f3ff00b3991a138e44198a16f94c7/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282", size = 1515304 }, + { url = "https://files.pythonhosted.org/packages/87/84/e8bd321aa99b72f48d4606fc5a0a920154125bd0a4608c67eab742dab087/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea", size = 1414712 }, + { url = "https://files.pythonhosted.org/packages/cd/cd/420e3fd1ac6977b008b72e7ad2dae6350cc84d4c5027fc390b024e61738f/pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2", size = 578113 }, + { url = "https://files.pythonhosted.org/packages/5c/57/73930d56ed45ae0cb4946f383f985c855c9b3d4063f26416998f07523c0e/pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971", size = 641631 }, + { url = "https://files.pythonhosted.org/packages/61/d2/ae6ac5c397f1ccad59031c64beaafce7a0d6182e0452cc48f1c9c87d2dd0/pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa", size = 543528 }, + { url = "https://files.pythonhosted.org/packages/12/20/de7442172f77f7c96299a0ac70e7d4fb78cd51eca67aa2cf552b66c14196/pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218", size = 1340639 }, + { url = "https://files.pythonhosted.org/packages/98/4d/5000468bd64c7910190ed0a6c76a1ca59a68189ec1f007c451dc181a22f4/pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4", size = 1008710 }, + { url = "https://files.pythonhosted.org/packages/e1/bf/c67fd638c2f9fbbab8090a3ee779370b97c82b84cc12d0c498b285d7b2c0/pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef", size = 673129 }, + { url = "https://files.pythonhosted.org/packages/86/94/99085a3f492aa538161cbf27246e8886ff850e113e0c294a5b8245f13b52/pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317", size = 910107 }, + { url = "https://files.pythonhosted.org/packages/31/1d/346809e8a9b999646d03f21096428453465b1bca5cd5c64ecd048d9ecb01/pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf", size = 867960 }, + { url = "https://files.pythonhosted.org/packages/ab/68/6fb6ae5551846ad5beca295b7bca32bf0a7ce19f135cb30e55fa2314e6b6/pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e", size = 869204 }, + { url = "https://files.pythonhosted.org/packages/0f/f9/18417771dee223ccf0f48e29adf8b4e25ba6d0e8285e33bcbce078070bc3/pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37", size = 1203351 }, + { url = "https://files.pythonhosted.org/packages/e0/46/f13e67fe0d4f8a2315782cbad50493de6203ea0d744610faf4d5f5b16e90/pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3", size = 1514204 }, + { url = "https://files.pythonhosted.org/packages/50/11/ddcf7343b7b7a226e0fc7b68cbf5a5bb56291fac07f5c3023bb4c319ebb4/pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6", size = 1414339 }, + { url = "https://files.pythonhosted.org/packages/01/14/1c18d7d5b7be2708f513f37c61bfadfa62161c10624f8733f1c8451b3509/pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4", size = 576928 }, + { url = "https://files.pythonhosted.org/packages/3b/1b/0a540edd75a41df14ec416a9a500b9fec66e554aac920d4c58fbd5756776/pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5", size = 642317 }, + { url = "https://files.pythonhosted.org/packages/98/77/1cbfec0358078a4c5add529d8a70892db1be900980cdb5dd0898b3d6ab9d/pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003", size = 543834 }, { url = "https://files.pythonhosted.org/packages/28/2f/78a766c8913ad62b28581777ac4ede50c6d9f249d39c2963e279524a1bbe/pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9", size = 1343105 }, { url = "https://files.pythonhosted.org/packages/b7/9c/4b1e2d3d4065be715e007fe063ec7885978fad285f87eae1436e6c3201f4/pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52", size = 1008365 }, { url = "https://files.pythonhosted.org/packages/4f/ef/5a23ec689ff36d7625b38d121ef15abfc3631a9aecb417baf7a4245e4124/pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08", size = 665923 }, @@ -1603,6 +1935,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/9a/10ed3c7f72b4c24e719c59359fbadd1a27556a28b36cdf1cd9e4fb7845d5/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306", size = 1183489 }, { url = "https://files.pythonhosted.org/packages/72/2d/8660892543fabf1fe41861efa222455811adac9f3c0818d6c3170a1153e3/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6", size = 1492932 }, { url = "https://files.pythonhosted.org/packages/7b/d6/32fd69744afb53995619bc5effa2a405ae0d343cd3e747d0fbc43fe894ee/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0", size = 1392485 }, + { url = "https://files.pythonhosted.org/packages/53/fb/36b2b2548286e9444e52fcd198760af99fd89102b5be50f0660fcfe902df/pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072", size = 906955 }, + { url = "https://files.pythonhosted.org/packages/77/8f/6ce54f8979a01656e894946db6299e2273fcee21c8e5fa57c6295ef11f57/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1", size = 565701 }, + { url = "https://files.pythonhosted.org/packages/ee/1c/bf8cd66730a866b16db8483286078892b7f6536f8c389fb46e4beba0a970/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d", size = 794312 }, + { url = "https://files.pythonhosted.org/packages/71/43/91fa4ff25bbfdc914ab6bafa0f03241d69370ef31a761d16bb859f346582/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca", size = 752775 }, + { url = "https://files.pythonhosted.org/packages/ec/d2/3b2ab40f455a256cb6672186bea95cd97b459ce4594050132d71e76f0d6f/pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c", size = 550762 }, ] [[package]] @@ -1611,6 +1948,37 @@ version = "2024.11.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } wheels = [ + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, + { url = "https://files.pythonhosted.org/packages/58/58/7e4d9493a66c88a7da6d205768119f51af0f684fe7be7bac8328e217a52c/regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", size = 482669 }, + { url = "https://files.pythonhosted.org/packages/34/4c/8f8e631fcdc2ff978609eaeef1d6994bf2f028b59d9ac67640ed051f1218/regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/c5/1b/f0e4d13e6adf866ce9b069e191f303a30ab1277e037037a365c3aad5cc9c/regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/25/4d/ab21047f446693887f25510887e6820b93f791992994f6498b0318904d4a/regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", size = 792121 }, + { url = "https://files.pythonhosted.org/packages/45/ee/c867e15cd894985cb32b731d89576c41a4642a57850c162490ea34b78c3b/regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", size = 831275 }, + { url = "https://files.pythonhosted.org/packages/b3/12/b0f480726cf1c60f6536fa5e1c95275a77624f3ac8fdccf79e6727499e28/regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", size = 818257 }, + { url = "https://files.pythonhosted.org/packages/bf/ce/0d0e61429f603bac433910d99ef1a02ce45a8967ffbe3cbee48599e62d88/regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", size = 792727 }, + { url = "https://files.pythonhosted.org/packages/e4/c1/243c83c53d4a419c1556f43777ccb552bccdf79d08fda3980e4e77dd9137/regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", size = 780667 }, + { url = "https://files.pythonhosted.org/packages/c5/f4/75eb0dd4ce4b37f04928987f1d22547ddaf6c4bae697623c1b05da67a8aa/regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", size = 776963 }, + { url = "https://files.pythonhosted.org/packages/16/5d/95c568574e630e141a69ff8a254c2f188b4398e813c40d49228c9bbd9875/regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", size = 784700 }, + { url = "https://files.pythonhosted.org/packages/8e/b5/f8495c7917f15cc6fee1e7f395e324ec3e00ab3c665a7dc9d27562fd5290/regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", size = 848592 }, + { url = "https://files.pythonhosted.org/packages/1c/80/6dd7118e8cb212c3c60b191b932dc57db93fb2e36fb9e0e92f72a5909af9/regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", size = 852929 }, + { url = "https://files.pythonhosted.org/packages/11/9b/5a05d2040297d2d254baf95eeeb6df83554e5e1df03bc1a6687fc4ba1f66/regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", size = 781213 }, + { url = "https://files.pythonhosted.org/packages/26/b7/b14e2440156ab39e0177506c08c18accaf2b8932e39fb092074de733d868/regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", size = 261734 }, + { url = "https://files.pythonhosted.org/packages/80/32/763a6cc01d21fb3819227a1cc3f60fd251c13c37c27a73b8ff4315433a8e/regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", size = 274052 }, { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, @@ -1660,27 +2028,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/5e/683c7ef7a696923223e7d95ca06755d6e2acbc5fd8382b2912a28008137c/ruff-0.8.3.tar.gz", hash = "sha256:5e7558304353b84279042fc584a4f4cb8a07ae79b2bf3da1a7551d960b5626d3", size = 3378522 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/c4/bfdbb8b9c419ff3b52479af8581026eeaac3764946fdb463dec043441b7d/ruff-0.8.3-py3-none-linux_armv6l.whl", hash = "sha256:8d5d273ffffff0acd3db5bf626d4b131aa5a5ada1276126231c4174543ce20d6", size = 10535860 }, - { url = "https://files.pythonhosted.org/packages/ef/c5/0aabdc9314b4b6f051168ac45227e2aa8e1c6d82718a547455e40c9c9faa/ruff-0.8.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e4d66a21de39f15c9757d00c50c8cdd20ac84f55684ca56def7891a025d7e939", size = 10346327 }, - { url = "https://files.pythonhosted.org/packages/1a/78/4843a59e7e7b398d6019cf91ab06502fd95397b99b2b858798fbab9151f5/ruff-0.8.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c356e770811858bd20832af696ff6c7e884701115094f427b64b25093d6d932d", size = 9942585 }, - { url = "https://files.pythonhosted.org/packages/91/5a/642ed8f1ba23ffc2dd347697e01eef3c42fad6ac76603be4a8c3a9d6311e/ruff-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c0a60a825e3e177116c84009d5ebaa90cf40dfab56e1358d1df4e29a9a14b13", size = 10797597 }, - { url = "https://files.pythonhosted.org/packages/30/25/2e654bc7226da09a49730a1a2ea6e89f843b362db80b4b2a7a4f948ac986/ruff-0.8.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fb782f4db39501210ac093c79c3de581d306624575eddd7e4e13747e61ba18", size = 10307244 }, - { url = "https://files.pythonhosted.org/packages/c0/2d/a224d56bcd4383583db53c2b8f410ebf1200866984aa6eb9b5a70f04e71f/ruff-0.8.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f26bc76a133ecb09a38b7868737eded6941b70a6d34ef53a4027e83913b6502", size = 11362439 }, - { url = "https://files.pythonhosted.org/packages/82/01/03e2857f9c371b8767d3e909f06a33bbdac880df17f17f93d6f6951c3381/ruff-0.8.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:01b14b2f72a37390c1b13477c1c02d53184f728be2f3ffc3ace5b44e9e87b90d", size = 12078538 }, - { url = "https://files.pythonhosted.org/packages/af/ae/ff7f97b355da16d748ceec50e1604a8215d3659b36b38025a922e0612e9b/ruff-0.8.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53babd6e63e31f4e96ec95ea0d962298f9f0d9cc5990a1bbb023a6baf2503a82", size = 11616172 }, - { url = "https://files.pythonhosted.org/packages/6a/d0/6156d4d1e53ebd17747049afe801c5d7e3014d9b2f398b9236fe36ba4320/ruff-0.8.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ae441ce4cf925b7f363d33cd6570c51435972d697e3e58928973994e56e1452", size = 12919886 }, - { url = "https://files.pythonhosted.org/packages/4e/84/affcb30bacb94f6036a128ad5de0e29f543d3f67ee42b490b17d68e44b8a/ruff-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7c65bc0cadce32255e93c57d57ecc2cca23149edd52714c0c5d6fa11ec328cd", size = 11212599 }, - { url = "https://files.pythonhosted.org/packages/60/b9/5694716bdefd8f73df7c0104334156c38fb0f77673d2966a5a1345bab94d/ruff-0.8.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5be450bb18f23f0edc5a4e5585c17a56ba88920d598f04a06bd9fd76d324cb20", size = 10784637 }, - { url = "https://files.pythonhosted.org/packages/24/7e/0e8f835103ac7da81c3663eedf79dec8359e9ae9a3b0d704bae50be59176/ruff-0.8.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8faeae3827eaa77f5721f09b9472a18c749139c891dbc17f45e72d8f2ca1f8fc", size = 10390591 }, - { url = "https://files.pythonhosted.org/packages/27/da/180ec771fc01c004045962ce017ca419a0281f4bfaf867ed0020f555b56e/ruff-0.8.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:db503486e1cf074b9808403991663e4277f5c664d3fe237ee0d994d1305bb060", size = 10894298 }, - { url = "https://files.pythonhosted.org/packages/6d/f8/29f241742ed3954eb2222314b02db29f531a15cab3238d1295e8657c5f18/ruff-0.8.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6567be9fb62fbd7a099209257fef4ad2c3153b60579818b31a23c886ed4147ea", size = 11275965 }, - { url = "https://files.pythonhosted.org/packages/79/e9/5b81dc9afc8a80884405b230b9429efeef76d04caead904bd213f453b973/ruff-0.8.3-py3-none-win32.whl", hash = "sha256:19048f2f878f3ee4583fc6cb23fb636e48c2635e30fb2022b3a1cd293402f964", size = 8807651 }, - { url = "https://files.pythonhosted.org/packages/ea/67/7291461066007617b59a707887b90e319b6a043c79b4d19979f86b7a20e7/ruff-0.8.3-py3-none-win_amd64.whl", hash = "sha256:f7df94f57d7418fa7c3ffb650757e0c2b96cf2501a0b192c18e4fb5571dfada9", size = 9625289 }, - { url = "https://files.pythonhosted.org/packages/03/8f/e4fa95288b81233356d9a9dcaed057e5b0adc6399aa8fd0f6d784041c9c3/ruff-0.8.3-py3-none-win_arm64.whl", hash = "sha256:fe2756edf68ea79707c8d68b78ca9a58ed9af22e430430491ee03e718b5e4936", size = 9078754 }, +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/37/9c02181ef38d55b77d97c68b78e705fd14c0de0e5d085202bb2b52ce5be9/ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", size = 3402103 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/67/f480bf2f2723b2e49af38ed2be75ccdb2798fca7d56279b585c8f553aaab/ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", size = 10546415 }, + { url = "https://files.pythonhosted.org/packages/eb/7a/5aba20312c73f1ce61814e520d1920edf68ca3b9c507bd84d8546a8ecaa8/ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac", size = 10346113 }, + { url = "https://files.pythonhosted.org/packages/76/f4/c41de22b3728486f0aa95383a44c42657b2db4062f3234ca36fc8cf52d8b/ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", size = 9943564 }, + { url = "https://files.pythonhosted.org/packages/0e/f0/afa0d2191af495ac82d4cbbfd7a94e3df6f62a04ca412033e073b871fc6d/ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", size = 10805522 }, + { url = "https://files.pythonhosted.org/packages/12/57/5d1e9a0fd0c228e663894e8e3a8e7063e5ee90f8e8e60cf2085f362bfa1a/ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", size = 10306763 }, + { url = "https://files.pythonhosted.org/packages/04/df/f069fdb02e408be8aac6853583572a2873f87f866fe8515de65873caf6b8/ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", size = 11359574 }, + { url = "https://files.pythonhosted.org/packages/d3/04/37c27494cd02e4a8315680debfc6dfabcb97e597c07cce0044db1f9dfbe2/ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", size = 12094851 }, + { url = "https://files.pythonhosted.org/packages/81/b1/c5d7fb68506cab9832d208d03ea4668da9a9887a4a392f4f328b1bf734ad/ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", size = 11655539 }, + { url = "https://files.pythonhosted.org/packages/ef/38/8f8f2c8898dc8a7a49bc340cf6f00226917f0f5cb489e37075bcb2ce3671/ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", size = 12912805 }, + { url = "https://files.pythonhosted.org/packages/06/dd/fa6660c279f4eb320788876d0cff4ea18d9af7d9ed7216d7bd66877468d0/ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", size = 11205976 }, + { url = "https://files.pythonhosted.org/packages/a8/d7/de94cc89833b5de455750686c17c9e10f4e1ab7ccdc5521b8fe911d1477e/ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", size = 10792039 }, + { url = "https://files.pythonhosted.org/packages/6d/15/3e4906559248bdbb74854af684314608297a05b996062c9d72e0ef7c7097/ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", size = 10400088 }, + { url = "https://files.pythonhosted.org/packages/a2/21/9ed4c0e8133cb4a87a18d470f534ad1a8a66d7bec493bcb8bda2d1a5d5be/ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", size = 10900814 }, + { url = "https://files.pythonhosted.org/packages/0d/5d/122a65a18955bd9da2616b69bc839351f8baf23b2805b543aa2f0aed72b5/ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", size = 11268828 }, + { url = "https://files.pythonhosted.org/packages/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 }, + { url = "https://files.pythonhosted.org/packages/10/98/ed6b56a30ee76771c193ff7ceeaf1d2acc98d33a1a27b8479cbdb5c17a23/ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", size = 9660086 }, + { url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 }, ] [[package]] @@ -1692,6 +2060,22 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/62/11/4d44a1f274e002784e4dbdb81e0ea96d2de2d1045b2132d5af62cc31fd28/scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417", size = 58620554 } wheels = [ + { url = "https://files.pythonhosted.org/packages/64/68/3bc0cfaf64ff507d82b1e5d5b64521df4c8bf7e22bc0b897827cbee9872c/scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389", size = 39069598 }, + { url = "https://files.pythonhosted.org/packages/43/a5/8d02f9c372790326ad405d94f04d4339482ec082455b9e6e288f7100513b/scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3", size = 29879676 }, + { url = "https://files.pythonhosted.org/packages/07/42/0e0bea9666fcbf2cb6ea0205db42c81b1f34d7b729ba251010edf9c80ebd/scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0", size = 23088696 }, + { url = "https://files.pythonhosted.org/packages/15/47/298ab6fef5ebf31b426560e978b8b8548421d4ed0bf99263e1eb44532306/scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3", size = 25470699 }, + { url = "https://files.pythonhosted.org/packages/d8/df/cdb6be5274bc694c4c22862ac3438cb04f360ed9df0aecee02ce0b798380/scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d", size = 35606631 }, + { url = "https://files.pythonhosted.org/packages/47/78/b0c2c23880dd1e99e938ad49ccfb011ae353758a2dc5ed7ee59baff684c3/scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69", size = 41178528 }, + { url = "https://files.pythonhosted.org/packages/5d/aa/994b45c34b897637b853ec04334afa55a85650a0d11dacfa67232260fb0a/scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad", size = 42784535 }, + { url = "https://files.pythonhosted.org/packages/e7/1c/8daa6df17a945cb1a2a1e3bae3c49643f7b3b94017ff01a4787064f03f84/scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5", size = 44772117 }, + { url = "https://files.pythonhosted.org/packages/b2/ab/070ccfabe870d9f105b04aee1e2860520460ef7ca0213172abfe871463b9/scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675", size = 39076999 }, + { url = "https://files.pythonhosted.org/packages/a7/c5/02ac82f9bb8f70818099df7e86c3ad28dae64e1347b421d8e3adf26acab6/scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2", size = 29894570 }, + { url = "https://files.pythonhosted.org/packages/ed/05/7f03e680cc5249c4f96c9e4e845acde08eb1aee5bc216eff8a089baa4ddb/scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617", size = 23103567 }, + { url = "https://files.pythonhosted.org/packages/5e/fc/9f1413bef53171f379d786aabc104d4abeea48ee84c553a3e3d8c9f96a9c/scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8", size = 25499102 }, + { url = "https://files.pythonhosted.org/packages/c2/4b/b44bee3c2ddc316b0159b3d87a3d467ef8d7edfd525e6f7364a62cd87d90/scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37", size = 35586346 }, + { url = "https://files.pythonhosted.org/packages/93/6b/701776d4bd6bdd9b629c387b5140f006185bd8ddea16788a44434376b98f/scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2", size = 41165244 }, + { url = "https://files.pythonhosted.org/packages/06/57/e6aa6f55729a8f245d8a6984f2855696c5992113a5dc789065020f8be753/scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2", size = 42817917 }, + { url = "https://files.pythonhosted.org/packages/ea/c2/5ecadc5fcccefaece775feadcd795060adf5c3b29a883bff0e678cfe89af/scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94", size = 44781033 }, { url = "https://files.pythonhosted.org/packages/c0/04/2bdacc8ac6387b15db6faa40295f8bd25eccf33f1f13e68a72dc3c60a99e/scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d", size = 39128781 }, { url = "https://files.pythonhosted.org/packages/c8/53/35b4d41f5fd42f5781dbd0dd6c05d35ba8aa75c84ecddc7d44756cd8da2e/scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07", size = 29939542 }, { url = "https://files.pythonhosted.org/packages/66/67/6ef192e0e4d77b20cc33a01e743b00bc9e68fb83b88e06e636d2619a8767/scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5", size = 23148375 }, @@ -1719,6 +2103,18 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/4a/89/0d20bac88016be35ff7d3c0c2ae64b477908f1b1dfa540c5d69ac7af07fe/shapely-2.0.6.tar.gz", hash = "sha256:997f6159b1484059ec239cacaa53467fd8b5564dabe186cd84ac2944663b0bf6", size = 282361 } wheels = [ + { url = "https://files.pythonhosted.org/packages/17/d4/f84bbbdb7771f5b9ade94db2398b256cf1471f1eb0ca8afbe0f6ca725d5a/shapely-2.0.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29a34e068da2d321e926b5073539fd2a1d4429a2c656bd63f0bd4c8f5b236d0b", size = 1449635 }, + { url = "https://files.pythonhosted.org/packages/03/10/bd6edb66ed0a845f0809f7ce653596f6fd9c6be675b3653872f47bf49f82/shapely-2.0.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c84c3f53144febf6af909d6b581bc05e8785d57e27f35ebaa5c1ab9baba13b", size = 1296756 }, + { url = "https://files.pythonhosted.org/packages/af/09/6374c11cb493a9970e8c04d7be25f578a37f6494a2fecfbed3a447b16b2c/shapely-2.0.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad2fae12dca8d2b727fa12b007e46fbc522148a584f5d6546c539f3464dccde", size = 2381960 }, + { url = "https://files.pythonhosted.org/packages/2b/a6/302e0d9c210ccf4d1ffadf7ab941797d3255dcd5f93daa73aaf116a4db39/shapely-2.0.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3304883bd82d44be1b27a9d17f1167fda8c7f5a02a897958d86c59ec69b705e", size = 2468133 }, + { url = "https://files.pythonhosted.org/packages/8c/be/e448681dc485f2931d4adee93d531fce93608a3ee59433303cc1a46e21a5/shapely-2.0.6-cp310-cp310-win32.whl", hash = "sha256:3ec3a0eab496b5e04633a39fa3d5eb5454628228201fb24903d38174ee34565e", size = 1294982 }, + { url = "https://files.pythonhosted.org/packages/cd/4c/6f4a6fc085e3be01c4c9de0117a2d373bf9fec5f0426cf4d5c94090a5a4d/shapely-2.0.6-cp310-cp310-win_amd64.whl", hash = "sha256:28f87cdf5308a514763a5c38de295544cb27429cfa655d50ed8431a4796090c4", size = 1441141 }, + { url = "https://files.pythonhosted.org/packages/37/15/269d8e1f7f658a37e61f7028683c546f520e4e7cedba1e32c77ff9d3a3c7/shapely-2.0.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5aeb0f51a9db176da9a30cb2f4329b6fbd1e26d359012bb0ac3d3c7781667a9e", size = 1449578 }, + { url = "https://files.pythonhosted.org/packages/37/63/e182e43081fffa0a2d970c480f2ef91647a6ab94098f61748c23c2a485f2/shapely-2.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a7a78b0d51257a367ee115f4d41ca4d46edbd0dd280f697a8092dd3989867b2", size = 1296792 }, + { url = "https://files.pythonhosted.org/packages/6e/5a/d019f69449329dcd517355444fdb9ddd58bec5e080b8bdba007e8e4c546d/shapely-2.0.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32c23d2f43d54029f986479f7c1f6e09c6b3a19353a3833c2ffb226fb63a855", size = 2443997 }, + { url = "https://files.pythonhosted.org/packages/25/aa/53f145e5a610a49af9ac49f2f1be1ec8659ebd5c393d66ac94e57c83b00e/shapely-2.0.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3dc9fb0eb56498912025f5eb352b5126f04801ed0e8bdbd867d21bdbfd7cbd0", size = 2528334 }, + { url = "https://files.pythonhosted.org/packages/64/64/0c7b0a22b416d36f6296b92bb4219d82b53d0a7c47e16fd0a4c85f2f117c/shapely-2.0.6-cp311-cp311-win32.whl", hash = "sha256:d93b7e0e71c9f095e09454bf18dad5ea716fb6ced5df3cb044564a00723f339d", size = 1294669 }, + { url = "https://files.pythonhosted.org/packages/b1/5a/6a67d929c467a1973b6bb9f0b00159cc343b02bf9a8d26db1abd2f87aa23/shapely-2.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:c02eb6bf4cfb9fe6568502e85bb2647921ee49171bcd2d4116c7b3109724ef9b", size = 1442032 }, { url = "https://files.pythonhosted.org/packages/46/77/efd9f9d4b6a762f976f8b082f54c9be16f63050389500fb52e4f6cc07c1a/shapely-2.0.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cec9193519940e9d1b86a3b4f5af9eb6910197d24af02f247afbfb47bcb3fab0", size = 1450326 }, { url = "https://files.pythonhosted.org/packages/68/53/5efa6e7a4036a94fe6276cf7bbb298afded51ca3396b03981ad680c8cc7d/shapely-2.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83b94a44ab04a90e88be69e7ddcc6f332da7c0a0ebb1156e1c4f568bbec983c3", size = 1298480 }, { url = "https://files.pythonhosted.org/packages/88/a2/1be1db4fc262e536465a52d4f19d85834724fedf2299a1b9836bc82fe8fa/shapely-2.0.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:537c4b2716d22c92036d00b34aac9d3775e3691f80c7aa517c2c290351f42cd8", size = 2439311 }, @@ -1754,117 +2150,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] -[[package]] -name = "snowballstemmer" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, -] - -[[package]] -name = "soupsieve" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, -] - -[[package]] -name = "sphinx" -version = "8.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, -] - -[[package]] -name = "sphinx-basic-ng" -version = "1.0.0b2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/0b/a866924ded68efec7a1759587a4e478aec7559d8165fac8b2ad1c0e774d6/sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9", size = 20736 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/dd/018ce05c532a22007ac58d4f45232514cd9d6dd0ee1dc374e309db830983/sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b", size = 22496 }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, -] - [[package]] name = "stack-data" version = "0.6.3" @@ -1879,15 +2164,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, ] -[[package]] -name = "tabulate" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, -] - [[package]] name = "tinycss2" version = "1.4.0" @@ -1900,6 +2176,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, ] +[[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 } +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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + [[package]] name = "tornado" version = "6.4.2" @@ -1918,6 +2233,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 }, ] +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + [[package]] name = "traitlets" version = "5.14.3" @@ -1960,12 +2287,20 @@ version = "6.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390 }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389 }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020 }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393 }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392 }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019 }, { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471 }, { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449 }, { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054 }, { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902 }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380 }, { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 },